#include "pch.hpp"
//===========================================================================//
// File:	 ErrorHandler.cpp												 //
// Project:  GameOS					                                         //
// Contents: Error Handling routines										 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 1/20/98   AG     Created													 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "ErrorHandler.hpp"
#include "Windows.hpp"
#include "Debugger.hpp"
#include "Texture Manager.hpp"
#include "3DRasterizer.hpp"
#include "Time.hpp"
#include "Dxrasterizer.hpp"
#include "Sound Renderer.hpp"
#include "VideoPlayback.hpp"

extern long StartSeed;
extern double ElapsedTime;
extern double TimeSlice;


BOOL (WINAPI* _GetOpenFileName)( LPOPENFILENAME lpofn );


// for memory tracking
extern HGOSHEAP ParentGameOSHeap;
HGOSHEAP gLogFileHeap = 0;
//
// Current log in progress
//
LogHeader		LogInfo;
//
// Pointer to current bitstream
//
DWORD*			BitPointer;
DWORD*			MaxBitPointer;
DWORD			CurrentShift;
DWORD			BitsAllZero;
const int		MaxLogSize=16384;

DWORD			FrameNumber;							// When recording=current frame, playback=last frame
//
// Playback/Record mode?
//
DWORD			LogMode=0;								// 0=Logging, 1=Playback, 2=Not logging or playing back (disabled)
//
// Number of times a log file has looped
//
DWORD			LogLoops=0;
//
// Pointer to base of current logfile memory block
//
LogStructure*	CurrentLogMemory;						// Pointer to current large block of ram


DWORD FrameAdvance;							// Used to step or fast forward through scripts
DWORD OldFreeze;								// Old value of gFreezeLogic
DWORD OldRender;								// Old value of gStopRendering;
DWORD Debounce=0;
DWORD Pressed=-1;















static gos_VERTEX ImageVertices[4];


void DrawImage( DWORD x, DWORD y, DWORD width, DWORD height, DWORD u, DWORD v, DWORD u1, DWORD v1 )
{
//
// generate correct u,v coords for button images
//
	float FontU=((float)u)*(1.0f/128.0f);
	float FontV=((float)v)*(1.0f/128.0f);
	float FontU1=((float)u1)*(1.0f/128.0f);
	float FontV1=((float)v1)*(1.0f/128.0f);
//
// Draw the image
//
	ImageVertices[0].x = (float)(x);
	ImageVertices[0].y = (float)(y);
	ImageVertices[0].u = FontU;
	ImageVertices[0].v = FontV;

	ImageVertices[1].x = (float)(x);
	ImageVertices[1].y = (float)(y+height);
	ImageVertices[1].u = FontU;
	ImageVertices[1].v = FontV1;

	ImageVertices[2].x = (float)(x+width);
	ImageVertices[2].y = (float)(y+height);
	ImageVertices[2].u = FontU1;
	ImageVertices[2].v = FontV1;

	ImageVertices[3].x = (float)(x+width);
	ImageVertices[3].y = (float)(y);
	ImageVertices[3].u = FontU1;
	ImageVertices[3].v = FontV;

	gos_ClipDrawQuad( ImageVertices );
}


typedef enum BUTTON
{
	STOP,
	PLAY,
	PAUSE,
	FORWARD,
	END,
};

//
// Draw button, 0=Play
//
//
void DrawButton( DWORD x, DWORD y, BUTTON Button, DWORD Pressed )
{
//
// Draw button image
//
//	DrawImage( x, y, 22, 22, Pressed*23, 97, Pressed*23+22, 97+22 );
	DrawImage( x, y, 22, 22, 105, 68+(Pressed*23), 105+22, 68+(Pressed*23)+22 );

//
// Move middle image in button if pressed
//
	x+=Pressed;
	y+=Pressed;
//
// Draw various button centers
//
	switch( Button )
	{
		case STOP:
			gos_SetRenderState( gos_State_Texture, 0 );
			DrawSquare( x+7, y+6, 7, 9, 0 );
			gos_SetRenderState( gos_State_Texture, FontTexture );
			break;
		case PLAY:
			DrawImage( x+14, y+6, -6, 9, 98, 68, 104, 77 );
			break;
		case PAUSE:
			DrawImage( x+9, y+6, 2, 9, 96, 68, 98, 77 );
			DrawImage( x+12, y+6, 2, 9, 96, 68, 98, 77 );
			break;
		case FORWARD:
			DrawImage( x+13, y+6,-6, 9, 98, 68, 104, 77 );
			DrawImage( x+19, y+6, -6, 9, 98, 68, 104, 77 );
			break;
		case END:
			DrawImage( x+10, y+6,-6, 9, 98, 68, 104, 77 );
			DrawImage( x+16, y+6, -6, 9, 98, 68, 104, 77 );
			DrawImage( x+19, y+6, -2, 9, 96, 68, 98, 77 );
			break;
	}

}



//
//
// During log file playback, show controls on the screen
//
//
void ShowLogFileControls()
{
//
// Hide controls when mouse on right edge of screen
//
	if( DBMouseX>=(Environment.screenWidth-2) )
		return;
//
// Do not effect debugger
//
	DWORD temp=DebuggerAlpha;
	DebuggerAlpha=0xff000000;
//
// Center log file controls in the middle of the screen
//
	DWORD gOffset;
	if( Environment.screenWidth<640 )
	{
		gos_SetRenderState( gos_State_Clipping, 2 );
		gOffset=Environment.screenWidth-640;
	}
	else
	{
		gos_SetRenderState( gos_State_Clipping, 0 );
		gOffset=(Environment.screenWidth-640)/2;
	}	
//
// Draw background for controls
//
	InitTextDisplay();
	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	DrawSquare( gOffset+20, 20, 600, 92, 0x004030 );
//
// Draw text
//
	char Buffer[128];
	gos_SetRenderState( gos_State_Texture, FontTexture );
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
//
// Display current frame number and length of file
//
	CurrentX=gOffset+30;
	CurrentY=30;
	DbChrX=0;
	DbChrY=0;
	DbMaxX=40;
	sprintf( Buffer, "%.2fs/%.2fs  %d/%d (%d)",(double)LogInfo.Current->CurrentTime, (double)LogInfo.Last->CurrentTime, FrameNumber, LogInfo.Frames, LogLoops );
	DrawText( 0xffffffff, Buffer );
//
// Display user and time grabbed
//
	sprintf( Buffer, "%s / %s", LogInfo.UserName, LogInfo.ExeDate );
	CurrentX=(gOffset+610)-6*strlen(Buffer);
	CurrentY=30;
	DbChrX=0;
	DbChrY=0;
	DbMaxX=40;
	DrawText( 0xffffffff, Buffer );
//
// Setup image texture drawing (part of standard font texture)
//
	gos_SetRenderState( gos_State_Texture, FontTexture );
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	memset( ImageVertices, 0, sizeof(ImageVertices) );
	ImageVertices[0].rhw	= 1.0f;
	ImageVertices[1].rhw	= 1.0f;
	ImageVertices[2].rhw	= 1.0f;
	ImageVertices[3].rhw	= 1.0f;
	ImageVertices[0].argb	= 0xffffffff;
	ImageVertices[1].argb	= 0xffffffff;
	ImageVertices[2].argb	= 0xffffffff;
	ImageVertices[3].argb	= 0xffffffff;
//
// Check for mouse pressing buttons
//
	if( DBButtons&3 &&
		DBMouseY>44 &&
		DBMouseY<(44+23) &&
		DBMouseX>(215+gOffset+39) &&
		DBMouseX<(215+gOffset+171) )
	{
		DWORD tx=(DBMouseX-(215+gOffset+40))/27;

		if( ((DBMouseX-(215+gOffset+40))-tx*27) < 22 )
		{
			if( Debounce )
			{
				if( Pressed!=tx )
				{
					Pressed=-1;
				}
			}
			else
			{
				Pressed=tx;
				Debounce=1;

				switch( Pressed )
				{
					case 0:					// STOP
						if( DBButtons&2 )
						{
							LogMode=2;
						}
						else
						{
							RestartGameOS();
						}
						break;
					case 1:					// PAUSE
						gFreezeLogic=!gFreezeLogic;
						if (gFreezeLogic)
						{
							SoundRendererPause();
						}
						else
						{
							SoundRendererContinue();
						}
						break;
					case 2:					// PLAY
						if( gFreezeLogic )
						{
							if( LogInfo.Frames==FrameNumber )			// Stop at end of log file
								RestartGameOS();
							OldFreeze=gFreezeLogic;
							OldRender=gStopRendering;
							FrameAdvance=1;
							if (OldFreeze)
							{
								DWORD sleepTime = (DWORD)(TimeSlice * 1000.0);
								SoundRendererContinue();
								VideoManagerContinue();
								Sleep(sleepTime);
								VideoManagerPause();
								SoundRendererPause();
							}
	
							gStopRendering=0;
							gFreezeLogic=0;
						}
						break;
					case 3:					// FORWARD
						if( LogInfo.Frames==FrameNumber )			// Stop at end of log file
							RestartGameOS();
						OldFreeze=gFreezeLogic;
						OldRender=gStopRendering;
						FrameAdvance=10;
						if( DBButtons&1 )
							Debounce=0;			// FORWARD repeats each loop on left button - right button forwards 10 then stops
						gStopRendering=1;
						gFreezeLogic=0;
						ShowFrame=1;
						break;
					case 4:					// END
						if( LogInfo.Frames==FrameNumber )			// Stop at end of log file
							RestartGameOS();
						OldFreeze=gFreezeLogic;
						OldRender=gStopRendering;
						FrameAdvance=-1;
						gStopRendering=1;
						gFreezeLogic=0;
						break;
				}
			}
		}
	}
	else
	{
		if( 0==(DBButtons&3) )
			Debounce=0;
		Pressed=-1;
	}
//
// Draw buttons
//
	DrawButton( 215+gOffset+40, 44, STOP, Pressed==0 );

	DrawButton( 215+gOffset+67, 44, PAUSE, Pressed==1 || gFreezeLogic );

	DrawButton( 215+gOffset+94, 44, PLAY, Pressed==2 );

	DrawButton( 215+gOffset+121, 44, FORWARD, Pressed==3 );

	DrawButton( 215+gOffset+148, 44, END, Pressed==4 );
//
// Draw scroll bar
//
	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	DrawSquare( gOffset+30, 70, 580, 16, 0x201010 );
	int BarLen=(578*FrameNumber)/(LogInfo.Frames);
	if( BarLen>0 )
		DrawSquare( gOffset+31, 71, BarLen, 14, 0x109010 );
	DebuggerAlpha=temp;
//
//
//
}







	













//
// Write out the current log file to disk and return true if success.
//
//
bool WriteLogFile( char* FileName )
{
	DWORD temp;
	HANDLE hFile = CreateFile( FileName, GENERIC_WRITE, 0,NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL );

	if( hFile!=INVALID_HANDLE_VALUE )
	{
//
// Make sure logfile is up to date
//
		EndLogging();
		LogInfo.Length=(BYTE*)LogInfo.Current-(BYTE*)CurrentLogMemory;
//
// Write out header
//
		WriteFile( hFile, &LogInfo, sizeof(LogInfo), &temp, NULL );
		if( temp!=sizeof(LogInfo))
		{
			CloseHandle( hFile );
			return false;
		}
//
// Write out all logfile blocks
//
		WriteFile( hFile, CurrentLogMemory, LogInfo.Length, &temp, NULL );

		if( temp!=LogInfo.Length)
		{
			CloseHandle( hFile );
			return false;
		}
//
// Close log file
//
		CloseHandle( hFile );
	}
	else
		return false;
	return true;
}








//
// Actually read in a log file from disk - return true if success
//
bool ReadLogFile( char* FileName, bool Prompt )
{
//
// If not valid, just return
//
	HANDLE hFile=CreateFile( FileName, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
	if( hFile==INVALID_HANDLE_VALUE )
		return false;
//
// Read in header
//
	LogHeader NewLogInfo;
	DWORD Size;
	ReadFile( hFile, &NewLogInfo, sizeof(NewLogInfo), &Size, NULL );
//
// Check whole file was read in
//
	if( Size!=sizeof(NewLogInfo) )
		STOP(( "Problem reading file '%s'",FileName ));
//
// Prompt user if this is the log file they want to load.
//
	if( NewLogInfo.Magic!=0x45AB1290 || NewLogInfo.Version!=LOGVERSION )
	{
		MessageBox( hWindow, "Log file format has changed", "Cannot load logfile", MB_OK );
		return false;
	}
//
// Check game is the same
//
	if( 0!=stricmp( NewLogInfo.GameName, ApplicationName ) )
	{
		char Buffer[64];
		sprintf( Buffer, "Log file was created for project '%s'", NewLogInfo.GameName );
		MessageBox( hWindow, Buffer, "Cannot load logfile", MB_OK );
		return false;
	}
//
// Now just warnings if .exe was different
//
	if( Prompt &&												// Not disabled prompting?
	  ( 0!=stricmp(NewLogInfo.AppPath, _pgmptr) ||				// .exe different?
		0!=stricmp(NewLogInfo.ExeDate, GetExeTime())) )			// time of exe different?
	{
		char Buffer[512];
		sprintf( Buffer, "Original executable %s\n"
						 "Original date stamp : %s\n\n"
						 "New executable %s\n"
						 "New date stamp : %s\n\n"
						 "Continue to load log file?",NewLogInfo.AppPath, NewLogInfo.ExeDate, _pgmptr, GetExeTime() );
		if( IDYES!=MessageBox( hWindow, Buffer, "Executeable Changed", MB_YESNO ) )
			return false;
	}
//
// Destroy current log file information
//
	if( CurrentLogMemory )
	{
		gos_Free( CurrentLogMemory );
		CurrentLogMemory=0;
	}
//
// Load in new log file information
//
	LogInfo=NewLogInfo;
	CurrentLogMemory=(LogStructure*)gos_Malloc( LogInfo.Length,  gLogFileHeap );
	ReadFile( hFile, CurrentLogMemory, LogInfo.Length, &Size, NULL );
//
// Check whole file was read in
//
	if( Size!=LogInfo.Length )
		STOP(( "Problem reading file '%s'",FileName ));
//
// Patch addresses to point to new log data
//
	LogInfo.Last=(LogStructure*)(((BYTE*)LogInfo.Last-(BYTE*)LogInfo.First)+(BYTE*)CurrentLogMemory);
	LogInfo.Current=LogInfo.First=CurrentLogMemory;
//
// Close file handle and return
//
	CloseHandle( hFile );
	return 1;
}





//
// Load in a log file and restart game
//
//
//
void LoadLogFile()
{
//
// Make sure game time is paused
//
	StartPauseTime();
//
// Make sure in window mode
//
	bool WasInFullScreenMode=0;
	if(Environment.fullScreen && hWindow )
	{
		WasInFullScreenMode=1;
		EnterWindowMode();						// Hide SPEW output when returning to window mode (make debug info easier to read!)
	}
	ShowCursor(TRUE);																											
//
// Ask user for file name
//
	OPENFILENAME OpenFileName;
	TCHAR szFile[MAX_PATH] = "";
	OpenFileName.lStructSize       = sizeof(OPENFILENAME);
	OpenFileName.hwndOwner         = hWindow;
	OpenFileName.lpstrFilter       = "Log File (*.log)\0*.log\0\0";

	OpenFileName.lpstrCustomFilter = NULL;
	OpenFileName.nMaxCustFilter    = 0;
	OpenFileName.nFilterIndex      = 0;
	OpenFileName.lpstrFile         = szFile;
	OpenFileName.nMaxFile          = sizeof(szFile);
	OpenFileName.lpstrFileTitle    = NULL;
	OpenFileName.nMaxFileTitle     = 0;
	OpenFileName.lpstrInitialDir   = NULL;
	OpenFileName.lpstrTitle        = "Load logfile";
	OpenFileName.nFileOffset       = 0;
	OpenFileName.nFileExtension    = 0;
	OpenFileName.lpstrDefExt       = "log";
	OpenFileName.Flags             = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_LONGNAMES;
//
// Get log file name
//
	HINSTANCE LibControl = LoadLibrary( "comdlg32.dll" );

	if( !LibControl )
		return;

	_GetOpenFileName = (BOOL (WINAPI*)( LPOPENFILENAME lpofn ))GetProcAddress( LibControl, "GetOpenFileNameA" ); 

	int result=_GetOpenFileName(&OpenFileName);
	
	FreeLibrary( LibControl );
//
// Success?
//
	if( !result)
	{
		ShowCursor(FALSE);				// Turn off cursor again
		if( WasInFullScreenMode )		// Re-enter full screen mode?
			EnterFullScreenMode();
		EndPauseTime();
		return;
	}
//
// Try and load the log file
//
	if( !ReadLogFile( OpenFileName.lpstrFile, 1 ) )
	{
		ShowCursor(FALSE);				// Turn off cursor again
		if( WasInFullScreenMode )		// Re-enter full screen mode?
			EnterFullScreenMode();
		EndPauseTime();
		return;
	}
//
// Hide cursor again
//
	ShowCursor(FALSE);				// Turn off cursor again
	if( WasInFullScreenMode )		// Re-enter full screen mode?
		EnterFullScreenMode();
//
// Loop game with new log file
//
	LogMode=1;
	DebuggerActive=0;
	InDebugger=0;
	RestartGameOS();
}












extern DWORD gStartWithLog;
extern char InitialLogFile[256];


//
// Start logging
//
void InitLogging()
{
#ifdef LAB_ONLY
//
//
//
	if( 0==gLogFileHeap )
		gLogFileHeap = gos_CreateMemoryHeap("Logging", 0, ParentGameOSHeap);
//
// Command line started with a log file?
//
	if( gStartWithLog )
	{
		if( ReadLogFile(InitialLogFile, 0) )
			LogMode=1;		// If read ok, start playing back from it.
		gStartWithLog=0;
	}
//
// Recording a log file
//
	if( 0==LogMode )
	{
//
// Setup an empty log file
//
		memset( &LogInfo, 0, sizeof(LogInfo) );
		LogInfo.Magic=0x45AB1290;
		LogInfo.Version=LOGVERSION;
		strcpy( LogInfo.GameName, ApplicationName );	// Game name
		strcpy( LogInfo.AppPath, _pgmptr );				// Application path
		strcpy( LogInfo.CommandLine, CommandLine );		// Command Line
		strncpy( LogInfo.UserName, gosGetUserName(), 31 );			// UserName
		strcpy( LogInfo.LogDate, GetTime() );			// Current Time/Date
		strcpy( LogInfo.ExeDate, GetExeTime() );		// Exe Time/Date
		LogInfo.StartSeed=StartSeed;
//
// Setup first memory block
//
		CurrentLogMemory=(LogStructure*)gos_Malloc(LOGBLOCKSIZE, gLogFileHeap);
		#ifndef _DEBUG
		memset( CurrentLogMemory, 0, LOGBLOCKSIZE );
		#endif
	}
//
// Playing back a log file
//
	else
	{
//
// Init log file playback variables
//
		LogInfo.Current=0;
		CommandLine=LogInfo.CommandLine;
		StartSeed=LogInfo.StartSeed;
		FrameNumber=0;
	}
//
// Make sure variables are correct
//
	UpDateLogFile();
	FrameAdvance=0;
	ElapsedTime = 0.0;
#endif
}




//
// Finish off current block
//
void EndLogging()
{
#ifdef LAB_ONLY
	if( 0==LogMode && LogInfo.Current && 0==LogInfo.Current->Length )
	{
		SPEW(( "GameOS_LogFile", "Endlog marker" ));
		WriteBitStream( 0xC3, 8, Log_Endmarker );

		LogInfo.Last=LogInfo.Current;

		DWORD result=(BYTE*)(BitPointer+1)-(BYTE*)LogInfo.Current-sizeof(LogStructure);

		if( result>MaxLogSize-4 )
		{
			PAUSE(( "Too much logging data in frame (%d bytes)",result ));
			LogMode=2;
			return;
		}

		if( BitsAllZero )
		{
			LogInfo.Current->Length=65535;
			LogInfo.Current++;;
		}
		else
		{
			LogInfo.Current->Length=(WORD)result;
			LogInfo.Current=(LogStructure*)(BitPointer+1);
		}
	}
	return;
#endif
}



//
// End and clean up memory used in logging
//
void DeleteLogging()
{
#ifdef LAB_ONLY
//
// If runLog is executing
//
	if( LoggingThreadID )
	{
		if( !ThreadDone )
		{
			TerminateThread( LoggingThreadID, 0 );
		}
		CloseHandle( LoggingThreadID );
		LoggingThreadID=0;
	}
//
// Delete any logging memory
//
	if( CurrentLogMemory )
	{
		gos_Free( CurrentLogMemory );
		CurrentLogMemory=0;
	}
	if ( gLogFileHeap )
		gos_DestroyMemoryHeap( gLogFileHeap );
	gLogFileHeap = 0;
#endif
}





extern double ElapsedTime;
extern double NonPausedElapsedTime;
extern double TimeSlice;
extern double PreviousTime;
extern float XPosition,YPosition, WheelPosition;
extern int XDelta,YDelta,WheelDelta;
extern DWORD ButtonsPressed;


//
// Called once per frame, play or record a log file
//
void UpDateLogFile()
{
#ifdef LAB_ONLY
	if( Platform!=Platform_Game )
		return;
//
// Recording a log file
//
	if( 0==LogMode )
	{
//
// Less than 1K left for next block?
//
		if( LogInfo.Current )
		{
//
// Check previous frame did not overrun and finish it off
//
			if( ( (BYTE*)LogInfo.Current+MaxLogSize) > ((BYTE*)CurrentLogMemory+LOGBLOCKSIZE) )
			{
				EndLogging();
				LogInfo.Current->Length=0;
				LogMode=2;
				return;
			}
			else
			{
				EndLogging();
			}
		}
		else
		{
			LogInfo.Current=CurrentLogMemory;						// 1st block
			LogInfo.First=LogInfo.Current;
		}
//
// Fill in variables for this frame
//
		LogInfo.Frames++;
		LogInfo.Current->Length=0;
		LogInfo.Current->CurrentTime=gos_GetElapsedTime();
		LogInfo.Current->NonPausedCurrentTime=gos_GetElapsedTime(1);
		DWORD Buttons;
		gos_GetMouseInfo(	&LogInfo.Current->MouseX,
							&LogInfo.Current->MouseY,
							&LogInfo.Current->pXDelta,
							&LogInfo.Current->pYDelta,
							&LogInfo.Current->pWheelDelta,
							&Buttons );
		LogInfo.Current->pButtonsPressed=(BYTE)Buttons;
		BitPointer=(DWORD*)((BYTE*)LogInfo.Current+sizeof(LogStructure));
		MaxBitPointer=BitPointer+((MaxLogSize-sizeof(LogStructure))/4);
		*BitPointer=0;
		CurrentShift=0;
		BitsAllZero=1;
	}
//
// Playing back a log file
//
	else if( 1==LogMode )
	{
//
// Do not increment frame if debugger has frozen game logic
//
		if( 0==gFreezeLogic && !(DebuggerActive && (!gGameLogicActive || gStopSystem)) )
		{
//
// Reached end of data?
//
			if( LogInfo.Frames==FrameNumber )
				RestartGameOS();
			FrameNumber++;
//
// Get current data block
//
			if( LogInfo.Current )
			{
				DWORD Len=LogInfo.Current->Length;
				if( 65535==Len )
					Len=0;
				LogInfo.Current=(LogStructure*)(Len+(BYTE*)LogInfo.Current+sizeof(LogStructure));
				TimeSlice = LogInfo.Current->CurrentTime - PreviousTime;
				PreviousTime = LogInfo.Current->CurrentTime;
			}
		}
		if( 0==LogInfo.Current )
			LogInfo.Current=LogInfo.First;
//
// Fill in variables for this frame
//
		ElapsedTime = LogInfo.Current->CurrentTime;
		NonPausedElapsedTime =  LogInfo.Current->NonPausedCurrentTime;
		BitPointer=(DWORD*)((BYTE*)LogInfo.Current+sizeof(LogStructure));
		if( 65535==LogInfo.Current->Length )
			BitsAllZero=1;
		else
			BitsAllZero=0;
		CurrentShift=0;
	}
#endif
}


//
// At end of game logic, check log file is valid during playback
//
void CheckLogInSync()
{
	#ifdef LAB_ONLY
		if( 0==LogMode )
		{
			SPEW(( "GameOS_LogFile", "Endlog Marker" ));
			WriteBitStream( 0xC3, 8, Log_Endmarker );
		}
		else if( 1==LogMode )
		{
			if( 0xC3!=ReadBitStream(8,Log_Endmarker) )
			{
				PAUSE(( "Log file has become out of sync!" ));
				LogMode=2;
			}
		}
	#endif
}




//
// Write to a logging file bit stream
//
void WriteBits( DWORD Value, DWORD Bits )
{
#ifdef LAB_ONLY
	if( Platform!=Platform_Game )
		return;

	if( LogMode!=0 )
		return;

	if( Value )
		BitsAllZero=0;						// Whole frame may have no bits set

	if( BitPointer+1>MaxBitPointer )
	{
		PAUSE(( "Too much logging data written in one game frame!" ));
		LogMode=2;
		return;
	}

	if( CurrentShift+Bits <= 32 )
	{
		*BitPointer|=(Value<<CurrentShift);
		CurrentShift+=Bits;
	}
	else
	{
		DWORD SomeBits=32-CurrentShift;
		if( SomeBits )
		{
			DWORD Mask=SomeBits==32 ? 0-1 : ((1<<SomeBits)-1);
			*BitPointer|=(Value&Mask)<<CurrentShift;
			BitPointer++;
			*BitPointer=(Value>>SomeBits);
			CurrentShift=Bits-SomeBits;
		}
		else
		{
			BitPointer++;
			*BitPointer=Value;
			CurrentShift=Bits;
		}
	}
#endif
}


//
//
//
void WriteBitStream( DWORD Value, DWORD Bits, int Type )
{
	WriteBits( Type, Log_Type );
	WriteBits( Value, Bits );
}

//
//
//
void WriteLogData( BYTE* pData, DWORD Length )
{
	WriteBitStream(Length,16,Log_DataBlock);
	WriteBits( Log_DataBlock, Log_Type );
	for( DWORD t0=0; t0<Length; t0++ )
	{
		WriteBits( *pData++, 8 );
	}
}





//
// Read from a logging file bit stream
//
DWORD ReadBits( DWORD Bits )
{
#ifdef LAB_ONLY
	if( BitsAllZero )
		return 0;						// Whole frame may have no bits set

	DWORD result;
	if( CurrentShift+Bits <= 32 )
	{
		DWORD Mask=Bits==32 ? 0-1 : ((1<<Bits)-1);
		result=(*BitPointer>>CurrentShift)&Mask;
		CurrentShift+=Bits;
	}
	else
	{
		DWORD SomeBits=32-CurrentShift;
		if( SomeBits )
		{
			DWORD Mask=SomeBits==32 ? 0-1 : ((1<<SomeBits)-1);
			result=(*BitPointer>>CurrentShift)&Mask;
			BitPointer++;
			Mask=(Bits-SomeBits==32) ? 0-1 : ((1<<(Bits-SomeBits))-1);
			result|=((*BitPointer&Mask)<<SomeBits);
			CurrentShift=Bits-SomeBits;
		}
		else
		{
			BitPointer++;
			DWORD Mask=Bits==32 ? 0-1 : ((1<<Bits)-1);
			result=*BitPointer&Mask;
			CurrentShift=Bits;
		}
	}
	return result;
#else
	return 0;
#endif
}


#ifdef LAB_ONLY
char* LogStrings[]={
	"Invalid",
	"Joystick Count",
	"Joystick Axis",
	"Joystick Button",
	"Joystick Info",
	"Joystick Effect",
	"Key status",
	"Key press",
	"End Marker",
	"Data block",
	"Network Info",
	"Media position"
	 };
#endif

DWORD ReadBitStream( DWORD Bits, int Type )
{
	#ifdef LAB_ONLY
	int temp=(int)ReadBits( Log_Type );

	if( temp!=Type )
	{
		if( temp>=sizeof(LogStrings)/sizeof(char*) )
			temp=0;
		
		PAUSE(( "Log data out of sync. Found \"%s\", was expecting \"%s\"", LogStrings[temp], LogStrings[Type] ));
		LogMode=2;
		return 0;
	}
	#endif
	return ReadBits( Bits );
}


//
//
//
void ReadLogData( BYTE* pData, DWORD Length )
{
	#ifdef LAB_ONLY
	int temp=(int)ReadBits( Log_Type );

	if( temp!=Log_DataBlock )
	{
		if( temp>=sizeof(LogStrings)/sizeof(char*) )
			temp=0;
		
		PAUSE(( "Log data out of sync. Found \"%s\", was expecting \"%s\"", LogStrings[temp], LogStrings[Log_DataBlock] ));
		LogMode=2;
		return;
	}
	#endif

	for( DWORD t0=0; t0<Length; t0++ )
	{
		*pData++=(BYTE)ReadBits( 8 );
	}
}


//
// Check for reaching the end of log files
//
void CheckLogFile()
{
	if( FrameAdvance )
	{
		if( LogInfo.Frames-1==FrameNumber )			// Stop at end of log file
		{
			gFreezeLogic=OldFreeze;
			gStopRendering=OldRender;
			Debounce=1;
			gFreezeLogic=1;
			SoundRendererPause();
			VideoManagerPause();
			FrameAdvance=0;
		}
		else
		{
			if( -1!=FrameAdvance )
				FrameAdvance--;
			if( FrameAdvance )
			{
				//
				// here's where we fast forward sounds
				// and movies
				//
				SoundRendererFF(TimeSlice);
				VideoManagerFF(TimeSlice);
				return;		   
			}
			gFreezeLogic=OldFreeze;
			gStopRendering=OldRender;
			if (gFreezeLogic)
			{
				SoundRendererPause();
			}
		}
	}
//
// If advancing a frame, halt when done
//
	if( DoingAdvance )
	{
		DoingAdvance--;
		gStopSystem=OldgStopSystem;
		gGameLogicActive=OldgGameLogicActive;
	}
}
