/*
**	Find - AmigaDOS 2.04 commodities utility
**
**	Copyright © 1991-1992 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "FindGlobal.h"

	/* A structure containing both a file name suffix and the
	 * approriate file type.
	 */

struct Suffix
{
	UBYTE	*Name;
	UBYTE	 Type;
};

	/* A table of valid ASCII characters (7 bits). */

STATIC BYTE ID_ValidTab[256] =
{
	0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,
	0,1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

	/* A table of clearly invalid ASCII characters (8 bits). */

STATIC BYTE ID_InvalidTab[256] =
{
	1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,
	1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

	/* Some file name suffixes for text files and the approriate
	 * file types.
	 */

STATIC struct Suffix TextSuffix[] =
{
	".C",		TYPE_C,
	".CPP",		TYPE_C,
	".CXX",		TYPE_C,
	".C++",		TYPE_C,
	".CC",		TYPE_C,
	".H",		TYPE_H,
	".ASM",		TYPE_ASM,
	".A",		TYPE_ASM,
	".S",		TYPE_ASM,
	".I",		TYPE_I,
	".BAS",		TYPE_BASIC,
	".GFA",		TYPE_BASIC,
	".REXX",	TYPE_REXX,
	".CED",		TYPE_REXX,
	".TTX",		TYPE_REXX,
	".VLT",		TYPE_REXX,
	".CPR",		TYPE_REXX,
	".TxEd",	TYPE_REXX,
	".ADPro",	TYPE_REXX,
	".TEX",		TYPE_TEX,
	".STY",		TYPE_TEX,
	".MF",		TYPE_METAFONT,
	".MOD",		TYPE_MOD,
	".DEF",		TYPE_MOD,
	".PS",		TYPE_POSTSCRIPT,
	".GUIDE",	TYPE_AMIGAGUIDE
};

	/* Some more file name suffixes for executable files and the
	 * approriate file types.
	 */

STATIC struct Suffix ExecutableSuffix[] =
{
	".device",	TYPE_DEVICE,
	".library",	TYPE_LIBRARY,
	"FileSystem",	TYPE_FILESYS,
	"Handler",	TYPE_HANDLER
};

	/* LongCompare(UBYTE *Buffer,ULONG Value):
	 *
	 *	Compare space in memory with a longword value.
	 */

STATIC BYTE __regargs
LongCompare(UBYTE *Buffer,ULONG Value)
{
	UBYTE *OtherBuffer = (UBYTE *)&Value,i;

	for(i = 0 ; i < 4 ; i++)
	{
		if(OtherBuffer[i] != Buffer[i])
			return(FALSE);
	}

	return(TRUE);
}

	/* Identify(UBYTE *Name):
	 *
	 *	Simple routine to identify a file type by looking at
	 *	its first 400 bytes. If successful a comment is
	 *	attached to the file describing the file type.
	 */

UBYTE __regargs
Identify(UBYTE *Name)
{
	ULONG	*Buffer;
	UBYTE	 Type = TYPE_FILE;
	WORD	 i,Len = strlen(Name);
	UBYTE	 IFFType[5];

		/* Allocate a buffer for the first 410 bytes of the
		 * file.
		 */

	if(Buffer = (ULONG *)AllocVec(410,MEMF_PUBLIC))
	{
		BPTR File,Size;

			/* Open the file. */

		if(File = Open(Name,MODE_OLDFILE))
		{
				/* Read the first 410 bytes. */

			if((Size = Read(File,Buffer,410)) >= sizeof(ULONG))
			{
				UBYTE	*CharBuffer = (UBYTE *)Buffer;
				WORD	 Count = 0;

					/* See if it's an ASCII file. */

				for(i = 0 ; i < Size ; i++)
				{
					if(ID_ValidTab[CharBuffer[i]])
						Count++;
					else
					{
						if(ID_InvalidTab[CharBuffer[i]])
						{
							Count = 0;

							break;
						}
					}
				}

					/* If more than 75% of the
					 * characters in the first
					 * 400 bytes are legal
					 * ASCII characters this
					 * file is supposed to be
					 * a text file.
					 */

				if(Count > 3 * (Size / 4))
					Type = TYPE_TEXT;

					/* Examine the first longword. */

				if(Type == TYPE_FILE)
				{
					switch(Buffer[0])
					{
						case 0x000003E7:Type = TYPE_OBJECT;
								break;

						case 0x000003F3:Type = TYPE_EXECUTABLE;
								break;

						case 0x000003FA:Type = TYPE_LIB;
								break;

						case 0xF7593647:Type = TYPE_TEXFONT;
								break;

						case 0xF7020183:Type = TYPE_TEXDVI;
								break;

						case 0xF7832020:Type = TYPE_GF;
								break;

						case 0x504B0304:Type = TYPE_ZIP;
								break;

						case 0x01030107:Type = TYPE_SPARCOBJECT;
								break;

						case 0x8103010B:Type = TYPE_SPARCEXECUTABLE;
								break;

						case 0x59A66A95:Type = TYPE_SUNRASTER;
								break;

						case 0x1F9D902A:Type = TYPE_COMPRESSED;
								break;

						case 0x30373037:Type = TYPE_CPIO;
								break;

						case 'FLIB':	Type = TYPE_FLIB;
								break;

						case 'FORM':	switch(Buffer[2])
								{
									case 'ILBM':	Type = TYPE_ILBM;
											break;

									case 'ANIM':	Type = TYPE_ANIM;
											break;

									case '8SVX':	Type = TYPE_8SVX;
											break;

									case 'SMUS':	Type = TYPE_SMUS;
											break;

									case 'FTXT':	Type = TYPE_FTXT;
											break;

									case 'PREF':	Type = TYPE_PREFS;
											break;

									case 'TERM':	Type = TYPE_TERM;
											break;

									case 'AVCF':	Type = TYPE_AMIGAVISION;
											break;

									default:	Type = TYPE_IFF;

											CopyMem(&Buffer[2],IFFType,4);

											IFFType[4] = 0;

											break;
								}

								break;

						case 'IMP!':	Type = TYPE_IMPLODER;
								break;

						case 'PP20':	Type = TYPE_POWERPACKER;
								break;

						case 'DMS!':	Type = TYPE_DMS;
								break;

						case 'Warp':	Type = TYPE_WARP;
								break;

						case 'ZOM5':
						case 'ZOOM':	Type = TYPE_ZOOM;
								break;

						case 'ZOO ':	Type = TYPE_ZOO;
								break;

						case 'GIF8':	Type = TYPE_GIF;
								break;

						default:	break;
					}
				}

					/* Now for oddly placed magic cookies... */

				if(Type == TYPE_FILE)
				{
						/* Dumb check for PCX (awful header -- just
						 * a single byte indicates that the file
						 * is supposed to be a PCX file! Those
						 * PC guys just don't have any
						 * decent software culture!).
						 */

					if(CharBuffer[0] == 0x0A)
						Type = TYPE_PCX;

						/* Yet another awful file format... */

					if(CharBuffer[0] == 'B' && CharBuffer[1] == 'M')
						Type = TYPE_BMP;

						/* Check for JFIF... */

					if(LongCompare(&CharBuffer[6],'JFIF'))
						Type = TYPE_JFIF;
				}

					/* Check for Macintosh data (note:
					 * this code assumes that the Mac
					 * binary header is still present,
					 * so if you are dealing with stripped
					 * binary files, don't expect the
					 * following code to work!).
					 */

				if(Type == TYPE_FILE)
				{
					UBYTE *ByteBuffer = &((UBYTE *)Buffer)[65];

						/* Check for MacPaint... */

					if(LongCompare(ByteBuffer,'PNTG'))
						Type = TYPE_MACPAINT;

						/* Check for SuperPaint... */

					if(LongCompare(ByteBuffer,'SPTG'))
						Type = TYPE_SUPERPAINT;

						/* Check for PICT... */

					if(LongCompare(ByteBuffer,'PICT'))
						Type = TYPE_PICT;

						/* Check for executable... */

					if(LongCompare(ByteBuffer,'APPL') || LongCompare(ByteBuffer,'PRES') || LongCompare(ByteBuffer,'FDOC') || LongCompare(ByteBuffer,'cdev') || LongCompare(ByteBuffer,'INIT'))
						Type = TYPE_MACEXECUTABLE;

						/* Check for StuffIt archive... */

					if(LongCompare(ByteBuffer,'SIT!') || LongCompare(ByteBuffer,'SIT2'))
						Type = TYPE_STUFFIT;

						/* Check for PackIt archive... */

					if(LongCompare(ByteBuffer,'PIT '))
						Type = TYPE_PACKIT;

						/* Check for self-extracting Compact archive. */

					if(LongCompare(ByteBuffer,'APPL') && LongCompare(&ByteBuffer[4],'EXTR'))
						Type = TYPE_COMPACT;

						/* Check for MacCompress archive. */

					if(LongCompare(ByteBuffer,'ZIVM'))
						Type = TYPE_MACCOMPRESS;

						/* Check for LhArc archive with MacBinary header. */

					if(LongCompare(ByteBuffer,'LARC'))
						Type = TYPE_LHARC;

						/* Check for Compact archive. */

					if(LongCompare(ByteBuffer,'PACT'))
						Type = TYPE_COMPACT;

						/* Check for Diamond archive. */

					if(LongCompare(ByteBuffer,'Pack'))
						Type = TYPE_DIAMOND;
				}

					/* Still no match? Have another try... */

				if(Type == TYPE_FILE)
				{
					if((Buffer[0] & 0x0000FFFF) == 0x00002D6C && (Buffer[1] & 0xFF00FF00) == 0x68002D00)
					{
						if(CharBuffer[5] == '5')
							Type = TYPE_LHA;
						else
							Type = TYPE_LHARC;
					}
					else
					{
						switch(Buffer[0] & 0xFFFF0000)
						{
							case 0x4D4D0000:
							case 0x49490000:	Type = TYPE_TIFF;
										break;

							case 0x1A080000:	Type = TYPE_ARC;
										break;

							case 0xEA600000:	Type = TYPE_ARJ;
										break;

							case 0x434A0000:	Type = TYPE_NEWMANX;
										break;

							case 0x414A0000:	Type = TYPE_OLDMANX;
										break;

							case 0x636A0000:	Type = TYPE_NEWMANXLIB;
										break;

							case 0x616A0000:	Type = TYPE_OLDMANXLIB;
										break;

							case 0xF5000000:	Type = TYPE_BASIC;
										break;

							case 0xE3100000:	Type = TYPE_ICON;
										break;

							case 0x4D5A0000:	Type = TYPE_MSDOSEXECUTABLE;
										break;

							case 0x601A0000:	Type = TYPE_ATARIEXECUTABLE;
										break;

							case 0x80000000:	Type = TYPE_DEGAS;
										break;

							default:		break;
						}
					}
				}

					/* Take a look at the file name
					 * suffixes.
					 */

				switch(Type)
				{
					case TYPE_TEXT:

							for(i = 0 ; i < sizeof(TextSuffix) / sizeof(struct Suffix) ; i++)
							{
								Size = strlen(TextSuffix[i] . Name);

								if(Len >= Size)
								{
									if(!Stricmp(&Name[Len - Size],TextSuffix[i] . Name))
									{
										Type = TextSuffix[i] . Type;
										break;
									}
								}
							}

							break;

					case TYPE_EXECUTABLE:

							for(i = 0 ; i < sizeof(ExecutableSuffix) / sizeof(struct Suffix) ; i++)
							{
								Size = strlen(ExecutableSuffix[i] . Name);

								if(Len >= Size)
								{
									if(!Stricmp(&Name[Len - Size],ExecutableSuffix[i] . Name))
									{
										Type = ExecutableSuffix[i] . Type;
										break;
									}
								}
							}

							if(Type == TYPE_EXECUTABLE)
							{
								if(Buffer[10] == 'LSFX')
									Type = TYPE_LHPAK;
							}

							if(Type == TYPE_EXECUTABLE)
							{
								if(Buffer[11] == 'SFX!')
									Type = TYPE_LHASFX;
							}

							break;

					case TYPE_OBJECT:

							if(Len >= 4)
							{
								if(!Stricmp(&Name[Len - 4],".LIB"))
									Type = TYPE_LIB;
							}

							break;

					default:	break;
				}
			}

			Close(File);
		}

		FreeVec(Buffer);
	}

	return(Type);
}
