#include "dodec.h"

struct StdListNode {
	struct Node StdNode;
	unsigned char Bytes[4];
};

unsigned char DebugFlag;

unsigned short Err;

struct List StdList;

extern void init_prng (unsigned long seed);
extern unsigned char prng (void);
extern unsigned char check_hl (unsigned long h, unsigned long l,
	unsigned char *values);
extern unsigned short mirror (unsigned short Word);

#pragma regcall (check_hl (d0, d1, a0))
#pragma regcall (mirror (d1))

unsigned char *modtable;
unsigned long prng_state[2];

struct Library *IntuitionBase, *GadToolsBase, *AslBase;

struct FileRequester *FileReq;

char SNameBuff[300], TNameBuff[300];

struct NewWindow NewWin = {
	60, 30, 400, 119,
	-1, -1,
	IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_GADGETDOWN | IDCMP_GADGETUP,
	WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SIMPLE_REFRESH |
		WFLG_ACTIVATE | WFLG_GIMMEZEROZERO,
	0, 0, (unsigned char *)"DODec - Directory Opus 4.0 Decrypter",
	NULL, NULL,
	402, 118, 402, 118,
	WBENCHSCREEN
};
struct Window *Win;

struct TextAttr TAttr = {
	(unsigned char *)"topaz.font", 8, 0, 0
};

struct NewGadget SourceGad = {
	320,  5, 64, 12, (unsigned char *)"Quelle", &TAttr, 1, 0, NULL, NULL };
struct NewGadget TargetGad = {
	320, 19, 64, 12, (unsigned char *)"Ziel",   &TAttr, 2, 0, NULL, NULL };
struct NewGadget StartGad = {
	320, 33, 64, 12, (unsigned char *)"Start",  &TAttr, 3, 0, NULL, NULL };
struct NewGadget StopGad = {
	320, 47, 64, 12, (unsigned char *)"Stop",   &TAttr, 4, 0, NULL, NULL };

struct NewGadget Byte1Gad = {
	255,  5, 48, 12, (unsigned char *)"#1:", &TAttr, 0, 0, NULL, NULL };
struct NewGadget Byte2Gad = {
	255, 19, 48, 12, (unsigned char *)"#2:", &TAttr, 0, 0, NULL, NULL };
struct NewGadget Byte3Gad = {
	255, 33, 48, 12, (unsigned char *)"#3:", &TAttr, 0, 0, NULL, NULL };
struct NewGadget Byte4Gad = {
	255, 47, 48, 12, (unsigned char *)"#4:", &TAttr, 0, 0, NULL, NULL };

struct NewGadget TypeGad = {
	10, 5, 200, 58, NULL, &TAttr, 5, 0, NULL, NULL };

struct NewGadget SNameGad = {
	80, 62, 304, 12, (unsigned char *)"Quelle:", &TAttr, 0, 0, NULL, NULL };
struct NewGadget TNameGad = {
	80, 76, 304, 12, (unsigned char *)"Ziel:",   &TAttr, 0, 0, NULL, NULL };
struct NewGadget StatusGad = {
	80, 90, 304, 12, (unsigned char *)"Status:", &TAttr, 0, 0, NULL, NULL };

struct TagItem NoTags[] = {
	{ TAG_END, 0 }
};

struct TagItem IntTags[] = {
	{ GTIN_MaxChars, 3 },
	{ TAG_END, 0 }
};

struct TagItem TextTags[] = {
	{ GTTX_Border, TRUE },
	{ TAG_END, 0 }
};

struct PTagItem {
	unsigned long pti_tag;
	unsigned char *pti_pointer;
};

struct PTagItem SourceTags[] = {
	{ ASLFR_TitleText, (unsigned char *)"Name der Quelldatei?" },
	{ TAG_END, 0 }
};

struct PTagItem TargetTags[]= {
	{ ASLFR_TitleText, (unsigned char *)"Name der Zieldatei?" },
	{ TAG_END, 0 }
};

struct PTagItem ListViewTags[] = {
	{ GTLV_Labels, (unsigned char *)&StdList },
	{ TAG_END, 0 }
};

struct Gadget *GadList = NULL, *SName, *TName, *Status, *Byte[4];

struct GadEntry {
	struct NewGadget *Point;
	unsigned long Type;
	struct TagItem *Tags;
	struct Gadget **GAddr;
} AllGadgets[] = {
	{ &SourceGad, BUTTON_KIND, NoTags, NULL },
	{ &TargetGad, BUTTON_KIND, NoTags, NULL },
	{ &StartGad, BUTTON_KIND, NoTags, NULL },
	{ &StopGad, BUTTON_KIND, NoTags, NULL },
	{ &Byte1Gad, INTEGER_KIND, IntTags, &Byte[0] },
	{ &Byte2Gad, INTEGER_KIND, IntTags, &Byte[1] },
	{ &Byte3Gad, INTEGER_KIND, IntTags, &Byte[2] },
	{ &Byte4Gad, INTEGER_KIND, IntTags, &Byte[3] },
	{ &TypeGad, LISTVIEW_KIND, (struct TagItem *)ListViewTags, NULL },
	{ &SNameGad, TEXT_KIND, TextTags, &SName },
	{ &TNameGad, TEXT_KIND, TextTags, &TName },
	{ &StatusGad, TEXT_KIND, TextTags, &Status },
	{ NULL, 0, NULL, NULL }
};

unsigned char *VInfo;

void DeleteWindow (void)
{
	FreeVisualInfo (VInfo);
	CloseWindow (Win);
	FreeGadgets (GadList);
}

short CreateWindow (void)
{
	struct GadEntry *CGad = AllGadgets;
	struct Gadget *SGad;

	if (SGad = CreateContext (&GadList))
		if (Win = OpenWindow (&NewWin))
			if (VInfo = GetVisualInfoA (Win -> WScreen, NoTags)) {
				do {
					CGad -> Point -> ng_VisualInfo = VInfo;
					SGad = CreateGadgetA (CGad -> Type, SGad, CGad -> Point,
						CGad -> Tags);
					if (CGad -> GAddr)
						*(CGad -> GAddr) = SGad;
				} while ((++CGad) -> Point);
				AddGList (Win, GadList, 0L, -1L, NULL);
				RefreshGList (GadList, Win, NULL, -1L);
				GT_RefreshWindow (Win, NULL);
				return TRUE;
			} else
				CloseWindow (Win);

	return FALSE;
}

void SetStat (char *String)
{
	struct PTagItem DummyTag[] = {
		{ GTTX_Text, NULL },
		{ TAG_END, NULL }
	};

	DummyTag -> pti_pointer = (unsigned char *)String;
	GT_SetGadgetAttrsA (Status, Win, NULL, (struct TagItem *)DummyTag);
}

void GetFile (struct TagItem *Tags, struct Gadget *UpGad, char *NameBuff)
{
	struct PTagItem DummyTag[] = {
		{ GTTX_Text, NULL },
		{ TAG_END, NULL }
	};

	AslRequest (FileReq, Tags);
	strncpy (NameBuff, (char *)FileReq -> fr_Drawer, 200);
	AddPart ((unsigned char *)NameBuff, FileReq -> fr_File, 299);
	DummyTag -> pti_pointer = (unsigned char *)NameBuff;
	GT_SetGadgetAttrsA (UpGad, Win, NULL, (struct TagItem *)DummyTag);
}

void ActSource (void)
{
	GetFile ((struct TagItem *)SourceTags, SName, SNameBuff);
}

void ActTarget (void)
{
	GetFile ((struct TagItem *)TargetTags, TName, TNameBuff);
}

void Decrypt (unsigned long Key)
{
	FILE *FHS, *FHT;
	char Buff[400];
	int c;

	SetStat ("Ich habe einen Schlüssel gefunden!");
	sprintf (Buff, "%s.%.8x", TNameBuff, Key);
	init_prng (Key);

	if (!(FHS = fopen (SNameBuff, "rb"))) {
		SetStat ("Ich kann die Quelldatei nicht öffnen.");
		Err++;
	} else {
		if (!(FHT = fopen (Buff, "wb"))) {
			SetStat ("Ich kann die Zieldatei nicht öffnen.");
			Err++;
		} else {
			while ((c = fgetc (FHS)) != EOF) {
				c -= prng ();
				if (fputc (c, FHT) == EOF) {
					SetStat ("Fehler beim Schreiben.");
					Err++;
					break;
				}
			}
			fclose (FHT);
		}
		fclose (FHS);
	}
}

short RefrOrAbort (void)
{
	struct IntuiMessage *IMsg;
	short RetVal = 0;

	while (IMsg = GT_GetIMsg (Win -> UserPort)) {
		switch (IMsg -> Class) {
			case IDCMP_REFRESHWINDOW:
				GT_BeginRefresh (Win);
				GT_EndRefresh (Win, (unsigned long)TRUE);
				break;
			case IDCMP_GADGETUP:
				if (((struct Gadget *)(IMsg -> IAddress)) -> GadgetID == 4)
					RetVal = 1;
				break;
		}
		GT_ReplyIMsg (IMsg);
	}
	return RetVal;
}

void ActStart (void)
{
	unsigned long i, Hi, Lo, Keys = 0;
	unsigned char Values[4];
	char Buff[100];
	FILE *FH;

	Err = 0;

	if (FH = fopen (SNameBuff, "rb")) {

		for (i = 0; i < 4; i++) {
			Values[i] = fgetc (FH) - ((struct StringInfo *)(Byte[i] -> SpecialInfo))
				-> LongInt;
		}
		fclose (FH);

		for (i = 0; i < 65536; i++) {
			sprintf (Buff, "Runde %5d, Anzahl: %d, Fehler: %d", i + 1, Keys, Err);
			SetStat (Buff);
			Lo = mirror ((unsigned short)i);

			if (RefrOrAbort ())
				break;

			for (Hi = 0; Hi < 65536; Hi++)
				if (check_hl (Hi, Lo, Values)) {
					Decrypt ((Hi << 16) | Lo);
					Keys++;
				}
		}
	SetStat ("Bereit.");
	} else
		SetStat ("Ich kann die Quelldatei nicht öffnen.");
}

void (*GadFuncs[])(void) = {
	ActSource, ActTarget, ActStart
};

short HandleIMsg (struct IntuiMessage *IMsg)
{
	unsigned short GadNum;

	switch (IMsg -> Class) {
		case IDCMP_REFRESHWINDOW:
			GT_BeginRefresh (Win);
			GT_EndRefresh (Win, (unsigned long)TRUE);
			break;
		case IDCMP_GADGETUP:
			if (GadNum = ((struct Gadget *)(IMsg -> IAddress))
				-> GadgetID)
				if (GadNum < 4)
					(*GadFuncs[GadNum - 1])();
				else
					if (GadNum != 4) {
						struct TagItem Dummy[] = {
							{ GTIN_Number, 0 }, { TAG_END, 0 } };
						unsigned short ListEl = IMsg -> Code;
						struct StdListNode *Scratch = (struct StdListNode *)
							StdList.lh_Head;

						while (ListEl--)
							Scratch = (struct StdListNode *)
								Scratch -> StdNode.ln_Succ;
						for (ListEl = 0; ListEl < 4; ListEl++) {
							Dummy -> ti_Data = Scratch -> Bytes[ListEl];
							GT_SetGadgetAttrsA (Byte[ListEl], Win, NULL, Dummy);
						}
					}
			break;
		case IDCMP_CLOSEWINDOW:
			return (TRUE);
		}

	return (FALSE);
}

char *Clone (char *Src)
{
	char *Dest = 0;

	if (Dest = AllocMem (strlen (Src) + 1, MEMF_PUBLIC))
		strcpy (Dest, Src);

	return Dest;
}

short ReadDefaults (void)
{
	FILE *fh;
	char NameBuff[80];
	unsigned int Byte1, Byte2, Byte3, Byte4; 
	struct StdListNode *NewNode;

	StdList.lh_Tail = NULL;
	StdList.lh_Head = (struct Node *)&StdList.lh_Tail;
	StdList.lh_TailPred = (struct Node *)&StdList.lh_Head;
	StdList.lh_Type = NT_UNKNOWN;

	if (!(fh = fopen ("dodec.def", "r")))
		if (!(fh = fopen ("s:dodec.def", "r")))
			return 0;

	while (fscanf (fh, "%s %d %d %d %d\n", NameBuff, &Byte1, &Byte2,
		&Byte3, &Byte4) != EOF) {
		if (DebugFlag)
			printf ("%s %d %d %d %d\n", NameBuff, Byte1, Byte2,
				Byte3, Byte4);
		if (!(NewNode = AllocMem (sizeof (*NewNode), MEMF_PUBLIC)))
			break;
		else {
			NewNode -> StdNode.ln_Pri = 0;
			if (NewNode -> StdNode.ln_Name = Clone (NameBuff)) {
				NewNode -> StdNode.ln_Type = NT_UNKNOWN;
				NewNode -> Bytes[0] = Byte1;
				NewNode -> Bytes[1] = Byte2;
				NewNode -> Bytes[2] = Byte3;
				NewNode -> Bytes[3] = Byte4;
				AddTail (&StdList, (struct Node *)NewNode);
			} else {
				FreeMem (NewNode, sizeof (*NewNode));
				break;
			}
		}
	}

	fclose (fh);
	return 1;
}

void FreeDefaults (void)
{
	struct StdListNode *Scratch;

	while (Scratch = (struct StdListNode *)RemHead (&StdList)) {
		FreeMem (Scratch -> StdNode.ln_Name,
			strlen (Scratch -> StdNode.ln_Name) + 1);
		FreeMem (Scratch, sizeof (*Scratch));
	}	
}

void InitMem (void)
{
	unsigned long i;

	for (i = 0; i < 32768; i++)
		modtable[i + 32768] = i % 9999;
	for (i = 32768; i < 65536; i++)
		modtable[i - 32768] = i % 9999;
}

int main (int argc, char *argv[])
{
	struct IntuiMessage *IMsg;

	if (argc == 2)
		DebugFlag = strcmp ("-d", argv[1]) ? FALSE : TRUE;

	if (!(IntuitionBase = OpenLibrary ((unsigned char *)"intuition.library",
		37L)))
		puts ("Ich benötige die Intuition-Library >= V37.");
	else {
		if (!(GadToolsBase = OpenLibrary ((unsigned char *)"gadtools.library",
			37L)))
			puts ("Ich benötige die Gadtools-Library >= V37.");
		else {
			if (!(AslBase = OpenLibrary ((unsigned char *)"asl.library", 37L)))
				puts ("Ich benötige die Asl-Library >= V37.");
			else {
				if (!(FileReq = (struct FileRequester *)
					AllocAslRequest (ASL_FileRequest, NoTags)))
					puts ("Ich kann den Filerequester nicht allozieren.");
				else {
					if (!(modtable = AllocMem (65536, MEMF_PUBLIC)))
						puts ("Ich kann keine 64 KByte allozieren.");
					else {
						InitMem ();
						if (!ReadDefaults ())
							puts ("Ich kann 'dodec.def' nicht einlesen.");
						else {
							if (!CreateWindow ())
								puts ("Ich kann das Programmfenster nicht öffnen.");
							else {
								short ExitHere = FALSE;
								SetStat ("Bereit.");
								do {
									WaitPort (Win -> UserPort);
									IMsg = GT_GetIMsg (Win -> UserPort);
									if (IMsg) {
										ExitHere = HandleIMsg (IMsg);
										GT_ReplyIMsg (IMsg);
									} else
										continue;
								} while (!ExitHere);
								DeleteWindow ();
							}
							FreeDefaults ();
						}
						FreeMem (modtable, 65536);
					}
					FreeAslRequest (FileReq);
				}
				CloseLibrary (AslBase);
			}
			CloseLibrary (GadToolsBase);
		}
		CloseLibrary (IntuitionBase);
	}
	return 0;
}
