
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "DeCSS.h"

#define MaxKeys 1000

typedef struct
	{
		int				occ;
		DVD40bitKey		key;
	} KeyOcc;

void Syntax(void)
{
	printf("SYNTAX ERROR: Wrong number of parameters.\n");
	printf(" DeCSSplus VOBInputFile [VOBOutputFile] [/p[ause]] [/v{0..9}] [/o[utput]] [/s]\n");
	printf("   /p : Pause at the end of execution\n");
	printf("   /v : Verbosity level 0..9\n");
	printf("   /o : Use VOBInputFile as output if no output file given\n");
	printf("   /s : Scan entier file. Default is to stop after having found 20 times the same key.\n\n");
	printf(" Please make sure the file is _readable_. Use a DVD-player to\n");
	printf(" remove the sector protection\n\n");
}

int main( int argc, char* argv[] ) {

int				paramPause = 0;
int				paramVerbose = 1;
int				paramOutput = 0;
int				paramScanAll = 0;
char			*paramInputFile = NULL;
char			*paramOutputFile = NULL;

FILE			*in,*out;
unsigned char	buf[0x800];
DVD40bitKey		MyKey;
int				pos,BytesRead,BytesWritten,BestPLen,BestP,i,j,k,encrypted=0,filsize;
KeyOcc			PosKey[MaxKeys];
int				RegisteredKeys = 0, TotalKeysFound = 0, StopScanning = 0;

	if (paramVerbose>=1)
	{
		printf(" DeCSSplus v1.0 - Decrypt without knowing the key - (c) 2000 Ethan Hawke\n");
		printf("-------------------------------------------------------------------------\n");
	}

	if (argc<2)
	{
		Syntax();
		if (paramPause)
		{
			printf("Press ENTER key to continue ...\n");
			getchar();
		}
		return(1);
	}
	i=1;
	while (i<argc)
	{
		if (strncmp(argv[i],"/",1)==0)
		{
			if (strncmp(argv[i],"/p",2)==0) paramPause = 1;
			else if (strncmp(argv[i],"/v",2)==0) paramVerbose = atoi((argv[i])+2);
			else if (strncmp(argv[i],"/s",2)==0) paramScanAll = 1;
			else if (strncmp(argv[i],"/o",2)==0) paramOutput = 1;
			else
			{
				Syntax();
				if (paramPause)
				{
					printf("Press ENTER key to continue ...\n");
					getchar();
				}
				return(1);
			}
		}
		else
		{
			if (!paramInputFile) paramInputFile = argv[i];
			else if (!paramOutputFile) { paramOutputFile = argv[i]; paramOutput = 1; }
			else
			{
				Syntax();
				if (paramPause)
				{
					printf("Press ENTER key to continue ...\n");
					getchar();
				}
				return(1);
			}
		}
		i++;
	}

	if (in = fopen(paramInputFile,"rb"))
	{
		pos = 0;
		fseek(in,0,SEEK_END);
		filsize = ftell(in);
		fseek(in,0,SEEK_SET);

		do
		{
			if (paramVerbose>=1 && filsize>1024*1024) printf("%.2f of file read & found %i keys...\r",pos*100.0/filsize,TotalKeysFound);
			BytesRead = fread(buf,1,0x800,in);
			if (buf[0x14] & 0x30) // PES_scrambling_control
			{
				encrypted = 1;
				BestPLen = 0;
				BestP = 0;
				for(i=2;i<0x30;i++)
				{
					for(j=i;(j<0x80) && (buf[0x7F-(j%i)]==buf[0x7F-j]);j++);
					if ((j>BestPLen) && (j>i))
					{
						BestPLen = j;
						BestP = i;
					}
				}
				if ((BestPLen>20) && (BestPLen/BestP>=2))
				{
					i = CSScrackerDVD(0,&buf[0x80],&buf[0x80-(BestPLen/BestP)*BestP],(DVD40bitKey*)&buf[0x54],&MyKey);
					while (i>=0)
					{
						k = 0;
						for(j=0;j<RegisteredKeys;j++)
							if (memcmp(&(PosKey[j].key),&MyKey,sizeof(DVD40bitKey))==0)
							{
								PosKey[j].occ++;
								TotalKeysFound++;
								k = 1;
							}
						if (k==0)
						{
							memcpy(&(PosKey[RegisteredKeys].key),&MyKey,sizeof(DVD40bitKey));
							PosKey[RegisteredKeys++].occ = 1;
							TotalKeysFound++;
						}

						if (paramVerbose>=2) printf("\nOfs:%08X - Key: %02X %02X %02X %02X %02X\n",pos,MyKey[0],MyKey[1],MyKey[2],MyKey[3],MyKey[4]);
						i = CSScrackerDVD(i,&buf[0x80],&buf[0x80-(BestPLen/BestP)*BestP],(DVD40bitKey*)&buf[0x54],&MyKey);
					}
					if (RegisteredKeys==1 && PosKey[0].occ>=20) StopScanning = 1;
				}
			}
			
			pos += BytesRead;
		} while (BytesRead==0x800 && !StopScanning);

		fclose(in);
		if (paramVerbose>=1 && StopScanning) printf("Found enough occurancies of the same key. Scan stopped.");
		if (paramVerbose>=1) printf("\n\n");
	}
	else
	{
		printf("FILE ERROR: File could not be opened. [Check if file is readable]\n");
		if (paramPause)
		{
			printf("Press ENTER key to continue ...\n");
			getchar();
		}
		return(1);
	}

	if (!encrypted)
	{
		printf("This file was _NOT_ encrypted!\n");
		if (paramPause)
		{
			printf("Press ENTER key to continue ...\n");
			getchar();
		}
		return(0);
	}

	if (encrypted && RegisteredKeys==0)
	{
		printf("Sorry... No keys found to this encrypted file.\n");
		if (paramPause)
		{
			printf("Press ENTER key to continue ...\n");
			getchar();
		}
		return(1);
	}

	for(i=0;i<RegisteredKeys-1;i++)
		for(j=i+1;j<RegisteredKeys;j++)
			if (PosKey[j].occ>PosKey[i].occ)
			{
				memcpy(&MyKey,&(PosKey[j].key),sizeof(DVD40bitKey));
				k = PosKey[j].occ;
				memcpy(&(PosKey[j].key),&(PosKey[i].key),sizeof(DVD40bitKey));
				PosKey[j].occ = PosKey[i].occ;
				memcpy(&(PosKey[i].key),&MyKey,sizeof(DVD40bitKey));
				PosKey[i].occ = k;
			}

	if (paramVerbose>=1)
	{
		printf(" Key(s) & key probability\n--------------------------\n");
		for(i=0;i<RegisteredKeys;i++)
			printf(" %02X %02X %02X %02X %02X - %3.2f%%\n",PosKey[i].key[0],PosKey[i].key[1],PosKey[i].key[2],PosKey[i].key[3],PosKey[i].key[4],PosKey[i].occ*100.0/TotalKeysFound);
		printf("\n");
	}

	if (paramOutput)
	{

		if (RegisteredKeys>1)
		{
			printf(" Which stream key do you want to use (ex. 13 47 8A BC EF): ");
			if (scanf("%2X %2X %2X %2X %2X",&(MyKey[0]),&(MyKey[1]),&(MyKey[2]),&(MyKey[3]),&(MyKey[4]))!=5)
			{
				printf("\nNot a valid key.\n");
				if (paramPause)
				{
					printf("Press ENTER key to continue ...\n");
					getchar();
				}
				return(1);
			}
			if (paramVerbose>=2) printf("Using key %02X %02X %02X %02X %02X\n",MyKey[0],MyKey[1],MyKey[2],MyKey[3],MyKey[4]);
		}
		else
			memcpy(&(MyKey),&(PosKey[0].key),sizeof(DVD40bitKey));

		if (paramOutputFile)
		{
			if (in = fopen(paramInputFile,"rb"))
			{
				if (out = fopen(paramOutputFile,"wb"))
				{
					pos = 0;
					do
					{
						if (paramVerbose>=1 && filsize>1024*1024) printf("%.2f of file read/written...\r",pos*100.0/filsize);
						BytesRead = fread(&buf,1,0x800,in);
						if (buf[0x14] & 0x30) // PES_scrambling_control
						{
							CSSdescrambleSector(&MyKey,(unsigned char*)&buf);
							buf[0x14] &= 0x8F;
						}
						BytesWritten = fwrite(&buf,1,BytesRead,out);
						if (BytesWritten!=BytesRead)
						{
							printf("Could not write to output file.\n");
							if (paramPause)
							{
								printf("Press ENTER key to continue ...\n");
								getchar();
							}
							return(1);
						}
						pos += BytesRead;
					} while (BytesRead==0x800);
				}
				else
				{
					printf("\n File could not be opened for Write.\n");
					if (paramPause)
					{
						printf("Press ENTER key to continue ...\n");
						getchar();
					}
					return(1);
				}
			}
			else
			{
				printf("\n File could not be opened for Read/Write.\n");
				if (paramPause)
				{
					printf("Press ENTER key to continue ...\n");
					getchar();
				}
				return(1);
			}
		}
		else
		{
			if (in = fopen(paramInputFile,"r+b"))
			{
				pos = 0;
				do
				{
					if (paramVerbose>=1 && filsize>1024*1024) printf("%.2f of file read/written...\r",pos*100.0/filsize);
					fseek(in,pos,SEEK_SET);
					BytesRead = fread(&buf,1,0x800,in);
					if (buf[0x14] & 0x30) // PES_scrambling_control
					{
						CSSdescrambleSector(&MyKey,(unsigned char*)&buf);
						buf[0x14] &= 0x8F;
					}
					fseek(in,pos,SEEK_SET);
					fwrite(&buf,1,BytesRead,in);				
					pos += BytesRead;
				} while (BytesRead==0x800);
			}
			else
			{
				printf("\n File could not be opened for Read/Write.\n");
				if (paramPause)
				{
					printf("Press ENTER key to continue ...\n");
					getchar();
				}
				return(1);
			}
		}

	}

	if (paramPause)
	{
		printf("Press ENTER key to continue ...\n");
		getchar();
	}
	return(0);
}
