
#include "VobReader.h"

unsigned char VobReader::_reverse[0x100];

unsigned char VobReader::_table[0x100] =
{
	0x33, 0x73, 0x3B, 0x26, 0x63, 0x23, 0x6B, 0x76, 0x3E, 0x7E, 0x36, 0x2B, 0x6E, 0x2E, 0x66, 0x7B,
	0xD3, 0x93, 0xDB, 0x06, 0x43, 0x03, 0x4B, 0x96, 0xDE, 0x9E, 0xD6, 0x0B, 0x4E, 0x0E, 0x46, 0x9B,
	0x57, 0x17, 0x5F, 0x82, 0xC7, 0x87, 0xCF, 0x12, 0x5A, 0x1A, 0x52, 0x8F, 0xCA, 0x8A, 0xC2, 0x1F,
	0xD9, 0x99, 0xD1, 0x00, 0x49, 0x09, 0x41, 0x90, 0xD8, 0x98, 0xD0, 0x01, 0x48, 0x08, 0x40, 0x91,
	0x3D, 0x7D, 0x35, 0x24, 0x6D, 0x2D, 0x65, 0x74, 0x3C, 0x7C, 0x34, 0x25, 0x6C, 0x2C, 0x64, 0x75,
	0xDD, 0x9D, 0xD5, 0x04, 0x4D, 0x0D, 0x45, 0x94, 0xDC, 0x9C, 0xD4, 0x05, 0x4C, 0x0C, 0x44, 0x95,
	0x59, 0x19, 0x51, 0x80, 0xC9, 0x89, 0xC1, 0x10, 0x58, 0x18, 0x50, 0x81, 0xC8, 0x88, 0xC0, 0x11,
	0xD7, 0x97, 0xDF, 0x02, 0x47, 0x07, 0x4F, 0x92, 0xDA, 0x9A, 0xD2, 0x0F, 0x4A, 0x0A, 0x42, 0x9F,
	0x53, 0x13, 0x5B, 0x86, 0xC3, 0x83, 0xCB, 0x16, 0x5E, 0x1E, 0x56, 0x8B, 0xCE, 0x8E, 0xC6, 0x1B,
	0xB3, 0xF3, 0xBB, 0xA6, 0xE3, 0xA3, 0xEB, 0xF6, 0xBE, 0xFE, 0xB6, 0xAB, 0xEE, 0xAE, 0xE6, 0xFB,
	0x37, 0x77, 0x3F, 0x22, 0x67, 0x27, 0x6F, 0x72, 0x3A, 0x7A, 0x32, 0x2F, 0x6A, 0x2A, 0x62, 0x7F,
	0xB9, 0xF9, 0xB1, 0xA0, 0xE9, 0xA9, 0xE1, 0xF0, 0xB8, 0xF8, 0xB0, 0xA1, 0xE8, 0xA8, 0xE0, 0xF1,
	0x5D, 0x1D, 0x55, 0x84, 0xCD, 0x8D, 0xC5, 0x14, 0x5C, 0x1C, 0x54, 0x85, 0xCC, 0x8C, 0xC4, 0x15,
	0xBD, 0xFD, 0xB5, 0xA4, 0xED, 0xAD, 0xE5, 0xF4, 0xBC, 0xFC, 0xB4, 0xA5, 0xEC, 0xAC, 0xE4, 0xF5,
	0x39, 0x79, 0x31, 0x20, 0x69, 0x29, 0x61, 0x70, 0x38, 0x78, 0x30, 0x21, 0x68, 0x28, 0x60, 0x71,
	0xB7, 0xF7, 0xBF, 0xA2, 0xE7, 0xA7, 0xEF, 0xF2, 0xBA, 0xFA, 0xB2, 0xAF, 0xEA, 0xAA, 0xE2, 0xFF,
};


namespace
{
	unsigned long readCode( unsigned char *p )
	{
		unsigned long x;

		x  = *p++; x <<= 8;
		x |= *p++; x <<= 8;
		x |= *p++; x <<= 8;
		x |= *p++;

		return x;
	}

	unsigned short readWord( unsigned char *p )
	{
		unsigned short x;

		x  = *p++; x <<= 8;
		x |= *p++;

		return x;
	}

	const unsigned long BLOCK_START_CODE  = 0x000001ba;
	const unsigned long BLOCK_CRYPT_MASK  = 0x30000000;
	const unsigned long VID_DETECT_BYTES  = 0x000001e0;
	const unsigned long AC3_DETECT_BYTES  = 0x000001bd;
	const unsigned long AUD_DETECT_BYTES0 = 0x000001c0;
	const unsigned long AUD_DETECT_BYTES1 = 0x000001c1;
	const unsigned long AUD_DETECT_BYTES2 = 0x000001c3;
	const unsigned long AUD_DETECT_BYTES3 = 0x000001c4;
	const unsigned long AUD_DETECT_BYTES4 = 0x000001c5;
	const unsigned long AUD_DETECT_BYTES5 = 0x000001c6;
	const unsigned long AUD_DETECT_BYTES6 = 0x000001c7;
	const unsigned long AUD_DETECT_BYTES7 = 0x000001c8;
	const unsigned long AUD_DETECT_BYTES8 = 0x000001c9;
	const unsigned long AUD_DETECT_BYTES9 = 0x000001ca;
	const unsigned long NAV_DETECT_BYTES  = 0x000001bb;
	const unsigned long PCI_DETECT_BYTES  = 0x000001bf;
};

VobReader::VobReader(messageCallback msgCb):
_messageCallback(msgCb),
_lba(0),
//_angles(false),
_skippingLbasForAngles(false),
_skipVobId(0),
_currentFile(NULL),
_buffer(new unsigned char[0x800]),
_keyComputed(false),
_ptReadStart(NULL),
_ptReadEnd(NULL),
_audFile(NULL),
IgnoreAudio( 0 ),
//SVE 03.06.2001 m_FileSize( 0 ),
//SVE 03.06.2001 m_FilePos( 0 ),
//SVE 03.06.2001 m_FilePosRead( 0 ),
BufferSize( 0 )
{
}

VobReader::~VobReader()
{
	delete [] _buffer;
	if (_currentFile)
		fclose(_currentFile);

	std::vector<FILE*>::iterator it = _ac3Files.begin();
	std::vector<FILE*>::iterator itEnd = _ac3Files.end();
	while (it != itEnd)
	{
		if (((*it) != NULL) && ((*it) != (FILE*)(-1)))
			fclose(*it);
		++it;
	}

	it = _audFile.begin();
	itEnd = _audFile.end();
	while (it != itEnd)
	{
		if (((*it) != NULL) && ((*it) != (FILE*)(-1)))
			fclose(*it);
		++it;
	}
}

bool
VobReader::addFile(const char* fileName)
{
	FILE * fcheck;
	AwaString f(fileName);
	f.upperCase();
//SVE 03.06.2001
	m_pRunState->fileCount=0;
	m_pRunState->curFile=-1;
	m_pRunState->fileSize=0;
	m_pRunState->fileSizeComplete=0;
	if (f.endWith(".LST"))
	{
		FILE* lstFile = fopen(fileName, "rt");
		if (lstFile == NULL)
		{
			message(AwaString("Cannot open the file '") + fileName + "' for reading", 1);
			return false;
		}

		while (!feof(lstFile))
		{
			char buffer[1000];
			if (fgets(buffer, 1000, lstFile) != NULL)
			{
				char* pt = buffer;
				while (*pt == ' ')
					++pt;
				if (pt[strlen(pt) - 1] == '\n')
					pt[strlen(pt) - 1] = 0;

				if ((*pt != 0) && (*pt != ';'))
				{
//SVE 03.06.2001	_files.push_back(buffer);
					fcheck=fopen(buffer,"rb");
					if ( fcheck )
					{
						fseek(fcheck, 0, SEEK_END );
//SVE 03.06.2001
						m_pRunState->fileNames[m_pRunState->fileCount]=(char*)malloc(strlen(buffer)+1);
						strcpy(m_pRunState->fileNames[m_pRunState->fileCount],buffer);
						m_pRunState->fileSizes[m_pRunState->fileCount]=ftell(fcheck);
						m_pRunState->fileSize += m_pRunState->fileSizes[m_pRunState->fileCount];
						m_pRunState->fileCount++;
//SVE 03.06.2001		m_FileSize += ftell(fcheck);
						fclose(fcheck);
					}
				}
			}
		}
	}
	else
	{
//SVE 03.06.2001	_files.push_back(fileName);
		fcheck=fopen(fileName,"rb");
		if ( fcheck )
		{
			fseek(fcheck, 0, SEEK_END );
//SVE 03.06.2001
			m_pRunState->fileNames[m_pRunState->fileCount]=(char*)malloc(strlen(fileName)+1);
			strcpy(m_pRunState->fileNames[m_pRunState->fileCount],fileName);
			m_pRunState->fileSizes[m_pRunState->fileCount]=ftell(fcheck);
			m_pRunState->fileSize += m_pRunState->fileSizes[m_pRunState->fileCount];
			m_pRunState->fileCount++;
//SVE 03.06.2001 m_FileSize += ftell(fcheck);
			fclose(fcheck);
		}
	}

	return true;
}

unsigned char*
VobReader::getFrame()
{
	while (true)
	{
		if (_currentFile == NULL)
		{
//SVE 03.06.2001			if (_files.empty())
			m_pRunState->filePos=0;
			m_pRunState->curFile++;
			if(m_pRunState->curFile)
				m_pRunState->fileSizeComplete+=m_pRunState->fileSizes[m_pRunState->curFile-1];
			m_pRunState->filePos=m_pRunState->fileSizeComplete; 
			if(m_pRunState->curFile>=m_pRunState->fileCount)
			{
//SVE 20.05.2001		message("No more files to parse", 1);
				return NULL;
			}

//SVE 03.06.2001			_currentFile = fopen(_files.front(), "rb");
			_currentFile = fopen(m_pRunState->fileNames[m_pRunState->curFile], "rb");
			if (_currentFile == NULL)
			{
//SVE 03.06.2001			message(AwaString("Cannot open the file '") + _files.front() + "' for reading", 1);
				message(AwaString("Cannot open the file '") + m_pRunState->fileNames[m_pRunState->curFile] + "' for reading", 1);
				return NULL;
			}

			if ( BufferSize > 0 )
				setvbuf( _currentFile, NULL, _IOFBF, BufferSize ); 

//SVE 03.06.2001			message(AwaString("\nParsing the file '") + _files.front() + "'", 1);
//SVE 03.06.2001			_currentFileName = _files.front();
//SVE 03.06.2001			_files.pop_front();
		}

		if (fread(_buffer, 1, 0x800, _currentFile) != 0x800)
		{
//SVE 03.06.2001			m_FilePosRead += ftell(_currentFile);
			fclose(_currentFile);
			_currentFile = NULL;
			continue;
		}

//SVe 03.06.2001		m_FilePos = ftell(_currentFile);
		m_pRunState->filePos=m_pRunState->fileSizeComplete+ftell(_currentFile);

		if(_buffer[0x14] & 0x10) // crypted
		{
			exit(0);
		}

		return _buffer;
	}
}


unsigned char*
VobReader::interpreteFrame(FrameType& type, long& stream, unsigned long& size)
{
	unsigned char* result = NULL;
	
	// Ok : here, we have to interpret the frame
	long code = readCode(_buffer);
	if (code == BLOCK_START_CODE)
	{
		unsigned char* pt = _buffer + 0x0e;
		
		long systemCode = readCode(pt);
		pt += 4;
		long packetLength = readWord(pt);
		pt += 2;
		unsigned char* endData = pt + packetLength;
		
		if (systemCode == NAV_DETECT_BYTES)
		{
			pt += packetLength;
			code = readCode(pt);
			pt += 4;
			if (code == PCI_DETECT_BYTES)
			{
				long localPacketLength = readWord(pt);
				pt += 2;
				pt += localPacketLength;
			}
			code = readCode(pt);
			pt += 4;
			if ( code == PCI_DETECT_BYTES)
			{
				long localPacketLength = readWord(pt);
				pt += 2;
				
				if (_skippingLbasForAngles)
				{
					if (pt[26] != _skipVobId)
						_skippingLbasForAngles = false;
				}
				else
				{
					if ((pt[41] != 0) || (pt[42] != 0))
					{
						if (pt[26] != _skipVobId)
							_skipVobId = pt[26] + 1;
						else
							_skippingLbasForAngles = true;
					}
				}
			}
		}
		else if (!_skippingLbasForAngles)
		{
			switch (systemCode)
			{
			case AC3_DETECT_BYTES:
				{
					type = FrameType::AC3;
					pt += 2; // pass the flags
					unsigned long nbBytesPassed = *pt;
					pt += (nbBytesPassed + 1);
					unsigned long substream = *pt;
					pt += 4;
					stream = substream - 0x80;
					if ((stream >= 0) && (stream <= 15))
					{
						result = pt;
						size = endData - pt;
					}
					else
						message("bad AC3 stream index: frame ignored", 3);
				}
				break;

			case VID_DETECT_BYTES:
				{
					type = FrameType::VID;
					pt += 2; // pass the flags
					unsigned long nbBytesPassed = *pt;
					pt += (nbBytesPassed + 1);
					result = pt;
					stream = 0;
					size = endData - pt;
				}
				break;

			case AUD_DETECT_BYTES0:
			case AUD_DETECT_BYTES1:
			case AUD_DETECT_BYTES2:
			case AUD_DETECT_BYTES3:
			case AUD_DETECT_BYTES4:
			case AUD_DETECT_BYTES5:
			case AUD_DETECT_BYTES6:
			case AUD_DETECT_BYTES7:
			case AUD_DETECT_BYTES8:
			case AUD_DETECT_BYTES9:
				{
					type = FrameType::AUD;
					pt += 2; // pass the flags
					unsigned long nbBytesPassed = *pt;
					pt += (nbBytesPassed + 1);
					result = pt;
					stream = systemCode-AUD_DETECT_BYTES0;
					size = endData - pt;
				}
				break;

			default:
				message("Unknown frame type", 3);
			}				
		}
	
		++_lba;
	}
	else
		message("Bad frame", 2);
	
	return result;
}

unsigned char*
VobReader::getInterpretedFrame(FrameType& type, long& stream, unsigned long& size)
{
	unsigned char* result = NULL;
	while (result == NULL)
	{
		unsigned char* frame = getFrame();
		if (frame == NULL)
			break;

		result = interpreteFrame(type, stream, size);
	}

	return result;
}


void
VobReader::message(const char* msg, int priority)
{
	if (_messageCallback)
		(*_messageCallback)(msg, priority);
}

void
VobReader::initDecode()
{
	int loop0, loop1, value;
	for(loop0 = 0; loop0 != 0x100; loop0++)
	{
		for(loop1 = value = 0; loop1 != 8; loop1++)
			value |= ((loop0 >> loop1) & 0x01) << (7 - loop1);

		_reverse[loop0] = (unsigned char) value;
	}
}

AwaString
VobReader::getStringKey()
{
	if (_keyComputed)
	{
		char buffer[200];
		sprintf(buffer, "%02X %02X %02X %02X %02X",
			_reverse[_lfsr0 >> 9],
			_reverse[_lfsr0 & 0xFF],
			_reverse[((_lfsr1 >> 16) & 0x1F) | ((_lfsr1 >> 17) & 0xE0)],
			_reverse[(_lfsr1 >> 8) & 0xFF],
			_reverse[_lfsr1 & 0xFF]);
		return buffer;
	}
	else
		return "";
}

void
VobReader::_ClockLfsr0Forward(int &lfsr0)
{
	int	temp;

	temp = (lfsr0 << 3) | (lfsr0 >> 14);
	lfsr0 = (lfsr0 >> 8) | ((((((temp << 3) ^ temp) << 3) ^ temp ^ lfsr0) & 0xFF) << 9);
}
	
void
VobReader::_ClockLfsr1Forward(int &lfsr1)
{
	lfsr1 = (lfsr1 >> 8) | ((((((((lfsr1 >> 8) ^ lfsr1) >> 1) ^ lfsr1) >> 3) ^ lfsr1) & 0xFF) << 17);
}
	
void
VobReader::_ClockBackward(int &lfsr0, int &lfsr1)
{
	int	temp0, temp1;

	lfsr0 = ((lfsr0 << 8) ^ ((((lfsr0 >> 3) ^ lfsr0) >> 6) & 0xFF)) & ((1 << 17) - 1);
	temp0 = ((lfsr1 >> 17) ^ (lfsr1 >> 4)) & 0xFF;
	temp1 = (lfsr1 << 5) | (temp0 >> 3);
	temp1 = ((temp1 >> 1) ^ temp1) & 0xFF;
	lfsr1 = ((lfsr1 << 8) | ((((((temp1 >> 2) ^ temp1) >> 1) ^ temp1) >> 3) ^ temp1 ^ temp0)) & ((1 << 25) - 1);
}
	
void
VobReader::_Salt(int &lfsr0, int &lfsr1, const unsigned char salt[5])
{
	lfsr0 ^= (_reverse[salt[0]] << 9) | _reverse[salt[1]];
	lfsr1 ^= ((_reverse[salt[2]] & 0xE0) << 17) | ((_reverse[salt[2]] & 0x1F) << 16) | (_reverse[salt[3]] << 8) | _reverse[salt[4]];
}
	
int
VobReader::_FindLfsr(const unsigned char *crypt, int offset, const unsigned char *plain, int &result0, int &result1)
{
	int		loop0, loop1, lfsr0, lfsr1, carry, count;
	
	for(loop0 = count = 0; loop0 != (1 << 18); loop0++) {
		lfsr0 = loop0 >> 1;
		carry = loop0 & 0x01;
		for(loop1 = lfsr1 = 0; loop1 != 4; loop1++) {
			_ClockLfsr0Forward(lfsr0);
			carry = (_table[crypt[offset + loop1]] ^ plain[loop1]) - ((lfsr0 >> 9) ^ 0xFF) - carry;
			lfsr1 = (lfsr1 >> 8) | ((carry & 0xFF) << 17);
			carry = (carry >> 8) & 0x01;
		}
		for(; loop1 != 7; loop1++) {
			_ClockLfsr0Forward(lfsr0);
			_ClockLfsr1Forward(lfsr1);
			carry += ((lfsr0 >> 9) ^ 0xFF) + (lfsr1 >> 17);
			if((carry & 0xFF) != (_table[crypt[offset + loop1]] ^ plain[loop1])) {
				break;
			}
			carry >>= 8;
		}
		if(loop1 == 7) {
			for(loop1 = 0; loop1 != 6; loop1++) {
				_ClockBackward(lfsr0, lfsr1);
			}
			carry = ((lfsr0 >> 9) ^ 0xFF) + (lfsr1 >> 17) + (loop0 & 0x01);
			if((carry & 0xFF) == (_table[crypt[offset]] ^ plain[0])) {
				for(loop1 = 0; loop1 != offset + 1; loop1++) {
					_ClockBackward(lfsr0, lfsr1);
				}
				if(lfsr0 & 0x100 && lfsr1 & 0x200000) {
					result0 = lfsr0;
					result1 = lfsr1;
					count++;
				}
			}
		}
	}
	return count;
}
	
bool
VobReader::_FindKey(const char *filename)
{
	return false;
}
	
void
VobReader::_Decrypt(unsigned char *buffer, int lfsr0, int lfsr1)
{
}

unsigned long
VobReader::getM2VBytes(unsigned char* buffer, long requestedSize, const char* outputDirectory)
{
	unsigned long sizeToRead = requestedSize;
	unsigned char* pt = buffer;

	while (true)
	{
		if (_ptReadStart == _ptReadEnd)
		{
			FrameType ft;
			long stream;
			unsigned long size;
			unsigned char* frame = getInterpretedFrame(ft, stream, size);
			if (frame == NULL)
				break;

			switch (ft)
			{
			case FrameType::AC3:
				if (outputDirectory&&IgnoreAudio>1)
				{
					for (int i = _ac3Files.size(); i <=stream; ++i)
						_ac3Files.push_back(NULL);
					if (_ac3Files[stream] == (FILE*)(-1))
						break;

					if ( IgnoreAudio==2 || stream == (IgnoreAudio-0x80))
					if (_ac3Files[stream] == NULL )
					{
						char buffer[50];
						sprintf(buffer, "stream0x%X.ac3", stream+0x80);
						AwaString ac3FileName(outputDirectory);
						ac3FileName += buffer;
						if ((_ac3Files[stream] = fopen(ac3FileName, "wb")) == NULL)
						{
							message(AwaString("Cannot open the file '") + ac3FileName + "' for writing", 1);
							_ac3Files[stream] = (FILE*)(-1);
							break;
						}
					}
					if ( IgnoreAudio==2 || stream == (IgnoreAudio-0x80))
						fwrite(frame, 1, size, _ac3Files[stream]);
				}
				break;

			case FrameType::VID:
				_ptReadStart = frame;
				_ptReadEnd = frame + size;
				break;

			case FrameType::AUD:
				if (outputDirectory&&IgnoreAudio>1)
				{
					for (int i = _audFile.size(); i <=stream; ++i)
						_audFile.push_back(NULL);

					if (_audFile[stream] == (FILE*)(-1))
						break;

					if ( IgnoreAudio==2 || stream == (IgnoreAudio-0xc0))
					if (_audFile[stream] == NULL )
					{
						char buffer[50];
						sprintf(buffer, "stream0x%X.mpa", stream+0xc0);
						AwaString mpeg2FileName(outputDirectory);
						mpeg2FileName += buffer;
						if ((_audFile[stream] = fopen(mpeg2FileName, "wb")) == NULL)
						{
							message(AwaString("Cannot open the file '") + mpeg2FileName + "' for writing", 1);
							_audFile[stream] = (FILE*)(-1);
							break;
						}
					}
					if ( IgnoreAudio==2 || stream == (IgnoreAudio-0xc0))
						fwrite(frame, 1, size, _audFile[stream]);
				}
				break;

			}
		}

		unsigned long sizeLeft = _ptReadEnd - _ptReadStart;
		if (sizeLeft > 0)
		{
			// there are some datas from previous packet
			if (sizeToRead <= sizeLeft)
			{
				memcpy(pt, _ptReadStart, sizeToRead);
				_ptReadStart += sizeToRead;
				sizeToRead = 0;
				break;
			}
			else
			{
				memcpy(pt, _ptReadStart, sizeLeft);
				_ptReadStart = _ptReadEnd;
				sizeToRead -= sizeLeft;
				pt += sizeLeft;
			}
		}
	}

	return requestedSize - sizeToRead;
}

unsigned long
VobReader::getLastLBA()
{
	return _lba;
}
