/*
 * Key Map Editor ver 1.0
 * Key Map IO
 *
 * by: Tim Friest
 * on: January 1, 1989
 */

#include "KME_Includes.h"
#include "KME_Protos.h"
#include "KME_Defs.h"
#include "KME_Globals.h"

#define	LongToByteArr(a, l)	a[3] = (l & 0x000000FF); a[2] = ((l & 0x0000FF00)>>8); a[1] = ((l & 0x00FF0000)>>16); a[0] = ((l & 0xFF000000)>>24)

#define	HUNK_HEADER	0x3F3
#define	HUNK_CODE	0x3E9
#define	HUNK_RELOC32	0x3EC
#define	HUNK_END	0x3F2

struct RelocNode {
	struct RelocNode *Next;
	ULONG Offset;
};

struct KeyMap_Hunk {
	ULONG	Hunk;
	ULONG	Length;
	struct KeyMapNode	kh_KeyMapNode;
	UBYTE	kh_LoKeyMapTypes[0x40];
	ULONG	kh_LoKeyMap[0x40];
	UBYTE	kh_LoCapsable[0x08];
	UBYTE	kh_LoRepeatable[0x08];
	UBYTE	kh_HiKeyMapTypes[0x38];
	ULONG	kh_HiKeyMap[0x38];
	UBYTE	kh_HiCapsable[0x07];
	UBYTE	kh_HiRepeatable[0x07];
};

struct KeyMap_Hunk *CodeSegment;
struct RelocNode *RelocList = NULL;

extern	struct KeyMapNode *ConvertKeyMap(struct KeyMapNode *);
extern	ULONG MakeCodeSeg(struct KeyMapNode *, char *);
extern	int	InsertRelocList(ULONG);

/*
 * KeyMapIO
 *  
 * parameters:	Filename - String pointer to filename
 *		Option   - IO option (Read or Write)
 */
void KeyMapIO(filename, Option)
char 	*filename;
int	Option;
{
	if (Option == LOAD) {
		if ((filename = ProcFileReq("Load")) != NULL) {
			if (!ReadKeyMap(filename))
				KeyMapNode = MakeNewKeyMap();
			UpdateDisplay(GadgetList, KeyMapNode, TRUE);
		}
		else
			if (!(CheckResource(RF_KeyMap)))
				KeyMapNode = MakeNewKeyMap();
				
	} else if (Option == SAVE) {
		if ((filename = ProcFileReq("Save")) != NULL)
			WriteKeyMap(filename);
	}
}

/*
 * ReadKeyMap
 *
 * parameter:	Filename - Filename of KeyMap to read
 * returns:	Result - Success of operation (TRUE or FALSE)
 */
int ReadKeyMap(filename)
char	*filename;
{
	if (CheckResource(RF_KeyMap)) {
		FreeKeyMap(KeyMapNode);
		ClearResource(RF_KeyMap);
	}
	if ((KeyMapSegment = LoadSeg(filename)) == NULL) {
		Write(Output(), "Error ReadKeyMap: LoadSeg failed\n", 33);
		return(FALSE);
	}
	if ((KeyMapNode = ConvertKeyMap((struct KeyMapNode *)BADDR((KeyMapSegment+1)))) == NULL) {
		Write(Output(), "Error ReadKeyMap: ConvertKeyMap Failed\n", 39);
		return(FALSE);
	}
	UnLoadSeg(KeyMapSegment);
	FlagResource(RF_KeyMap);
	return(TRUE);
}

/*
 * Convert Key Map Strings to freememable places
 *
 * parameters:	KeyMapNode - Key Map Node structure
 *
 * returns:	Success - TRUE or FALSE
 */
struct KeyMapNode *ConvertKeyMap(OldKeyMapNode)
struct KeyMapNode *OldKeyMapNode;
{
	USHORT KeyCode;
	struct HalfKeyMap *HalfKeyMap;
	int length, maphalf;
	UBYTE len, HeaderLen;
	struct KeyMapNode *NewKeyMapNode;
	UBYTE *old, *new;

	if ((NewKeyMapNode = (struct KeyMapNode *)AllocMem((KeyNodeSize + KeyMapSize), MEMF_CLEAR)) == NULL) {
		Write(Output(), "Memory Allocation failed, ConvertKeyMap:KeyMap\n", 47);
		return(NULL);
	}
	memcpy((UBYTE *)NewKeyMapNode, (UBYTE *)OldKeyMapNode, KeyNodeSize);
	new = ((UBYTE *)NewKeyMapNode) + KeyNodeSize;
	memcpy(new, OldKeyMapNode->kn_KeyMap.km_LoKeyMapTypes, 0x40);
	NewKeyMapNode->kn_KeyMap.km_LoKeyMapTypes = new;
	new += 0x40;
	memcpy(new, (UBYTE *)OldKeyMapNode->kn_KeyMap.km_LoKeyMap, (0x40*4));
	NewKeyMapNode->kn_KeyMap.km_LoKeyMap = (long *)new;
	new += (0x40 * 4);
	memcpy(new, OldKeyMapNode->kn_KeyMap.km_LoCapsable, 0x08);
	NewKeyMapNode->kn_KeyMap.km_LoCapsable = new;
	new += 0x08;
	memcpy(new, OldKeyMapNode->kn_KeyMap.km_LoRepeatable, 0x08);
	NewKeyMapNode->kn_KeyMap.km_LoRepeatable = new;
	new += 0x08;
	memcpy(new, OldKeyMapNode->kn_KeyMap.km_HiKeyMapTypes, 0x38);
	NewKeyMapNode->kn_KeyMap.km_HiKeyMapTypes = new;
	new += 0x38;
	memcpy(new, (UBYTE *)OldKeyMapNode->kn_KeyMap.km_HiKeyMap, (0x38*4));
	NewKeyMapNode->kn_KeyMap.km_HiKeyMap = (long *)new;
	new += (0x38 * 4);
	memcpy(new, OldKeyMapNode->kn_KeyMap.km_HiCapsable, 0x07);
	NewKeyMapNode->kn_KeyMap.km_HiCapsable = new;
	new += 0x07;
	memcpy(new, OldKeyMapNode->kn_KeyMap.km_HiRepeatable, 0x07);
	NewKeyMapNode->kn_KeyMap.km_HiRepeatable = new;

	HalfKeyMap = (struct HalfKeyMap *)&NewKeyMapNode->kn_KeyMap;
	maphalf = 0;
	KeyCode = 0x00;
	while (maphalf < 2)
		if (((maphalf == 0) && (KeyCode < 0x40)) || ((maphalf == 1) && (KeyCode < 0x38))) {
 			switch (HalfKeyMap->KeyMapTypes[KeyCode] & (KCF_STRING|KCF_DEAD|KCF_NOP)) {
				case KCF_STRING:
					len = StringKeyLength(HalfKeyMap, KeyCode, &HeaderLen);
					if ((new = (UBYTE *)AllocMem(len, MEMF_CLEAR)) == NULL) {
						Write(Output(), "Memory allocation failed, ConvertKeyMap:String\n", 47);
						return(NULL);
					}
					CopyStringKey(HalfKeyMap, KeyCode, new, HeaderLen);
					HalfKeyMap->KeyMap[KeyCode] = (ULONG)new;
					break;
				case KCF_DEAD:
					len = DeadKeyLength(HalfKeyMap, KeyCode, &HeaderLen);
					if ((new = (UBYTE *)AllocMem(len, MEMF_CLEAR)) == NULL) {
						Write(Output(), "Memory allocation failed, ConvertKeyMap:Dead\n", 46);
						return(NULL);
					}
					CopyDeadKey(HalfKeyMap, KeyCode, new, HeaderLen);
					HalfKeyMap->KeyMap[KeyCode] = (ULONG)new;
			} /* switch */
			KeyCode += 1;
		}
		else {
			KeyCode = 0x00;
			HalfKeyMap = (struct HalfKeyMap *)&NewKeyMapNode->kn_KeyMap.km_HiKeyMapTypes;
			maphalf += 1;
		}
	old = OldKeyMapNode->kn_Node.ln_Name;
	length = strlen(old) + 1;
	if ((new = (UBYTE *)AllocMem(length, MEMF_CLEAR)) == NULL) {
		Write(Output(), "Memory Allocation Failed, ConvertKeyMap:NodeName\n", 49);
		return(NULL);
	}
	strcpy(new, old);
	NewKeyMapNode->kn_Node.ln_Name = (char *)new;
	return(NewKeyMapNode);
}

/*
 * Free Key Map
 *
 * parameter:	KeyMapNode - Key Map Node to free
 */
void FreeKeyMap(FreeKeyMapNode)
struct KeyMapNode *FreeKeyMapNode;
{
	UBYTE *buff;
	UBYTE len, headerlen;
	struct HalfKeyMap *HalfKeyMap;
	USHORT KeyCode;
	int maphalf, length;

	if ((buff = FreeKeyMapNode->kn_Node.ln_Name) != NULL) {
		length = strlen(buff) + 1;
		FreeMem(buff, length);
	}
	HalfKeyMap = (struct HalfKeyMap *)&FreeKeyMapNode->kn_KeyMap.km_HiKeyMapTypes;
	maphalf = 1;
	KeyCode = 0x37;
	while (maphalf >= 0) {
		KeyCode -= 1;
		switch (HalfKeyMap->KeyMapTypes[KeyCode] & (KCF_STRING|KCF_DEAD|KCF_NOP)) {
			case KCF_STRING:
				buff = (UBYTE *)HalfKeyMap->KeyMap[KeyCode];
				len = StringKeyLength(HalfKeyMap, KeyCode, &headerlen);
				FreeMem(buff, len);
				break;
			case KCF_DEAD:
				buff = (UBYTE *)HalfKeyMap->KeyMap[KeyCode];
				len = DeadKeyLength(HalfKeyMap, KeyCode, &headerlen);
				FreeMem(buff, len);
		} /* switch */
		if (KeyCode == 0x00) {
			KeyCode = 0x3F;
			HalfKeyMap = (struct HalfKeyMap *)&FreeKeyMapNode->kn_KeyMap;
			maphalf -= 1;
		}
	} /* while */
	FreeMem((UBYTE *)FreeKeyMapNode, (KeyNodeSize + KeyMapSize));
	ClearFlag(RF_KeyMap);
}

/*
 * WriteKeyMap
 *
 * parameter:	Filename - Filename of KeyMap to read
 * returns:	Result - Success of operation (TRUE or FALSE)
 */
int WriteKeyMap(filename)
char	*filename;
{
	BPTR File;
	ULONG CodeLen, RelocLen;
	struct RelocNode *temp, *next;
	UBYTE bytes[4];
	int status = TRUE;

	if ((CodeLen = MakeCodeSeg(KeyMapNode, filename)) == NULL) {
		Write(Output(), "HUNK_CODE Creation Error\n", 24);
		goto EndWrite;
	}
	if ((File = Open(filename, MODE_NEWFILE)) == NULL)
		return(FALSE);
/* Creating an executable HUNK */
	LongToByteArr(bytes, HUNK_HEADER);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Header\n", 25);
		status = FALSE;
		goto EndWrite;
	}
/* No libraries used */
	LongToByteArr(bytes, 0);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Header libname\n", 34);
		status = FALSE;
		goto EndWrite;
	}
/* Hunk table size */
	LongToByteArr(bytes, 1);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Header table\n", 31);
		status = FALSE;
		goto EndWrite;
	}
/* First hunk */
	LongToByteArr(bytes, 0);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Header first hunk\n", 36);
		status = FALSE;
		goto EndWrite;
	}
/* Last hunk */
	LongToByteArr(bytes, 0);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Header last hunk\n", 35);
		status = FALSE;
		goto EndWrite;
	}
/* write Hunk sizes (number of longwords) */
	LongToByteArr(bytes, ((CodeLen>>2)-2));
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Header sizes\n", 31);
		status = FALSE;
		goto EndWrite;
	}
/* write HUNK_CODE */
	if (Write(File, (UBYTE *)CodeSegment, CodeLen) != CodeLen) {
		Write(Output(), "Write Error, HUNK_CODE\n", 22);
		status = FALSE;
		goto EndWrite;
	}
/* write Hunk_Reloc32 */
	LongToByteArr(bytes, HUNK_RELOC32);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Reloc32\n", 26);
		status = FALSE;
		goto EndWrite;
	}
	for (temp = RelocList, RelocLen = 0; temp; (temp = temp->Next), RelocLen++);
/* write hunk_reloc32 length (number of longwords) */
	if (Write(File, (UBYTE *)&RelocLen, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Reloc32 Length\n", 33);
		status = FALSE;
		goto EndWrite;
	}
/* write hunk_reloc32 hunk number effected */
	LongToByteArr(bytes, 0);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Reloc32 hunk number\n", 38);
		status = FALSE;
		goto EndWrite;
	}
/* write hunk_reloc32 offsets */
	for (temp = RelocList; temp; temp = temp->Next)
		if (Write(File, (UBYTE *)&temp->Offset, 4) != 4) {
			Write(Output(), "Write Error, Hunk_Reloc32 offset\n", 33);
			status = FALSE;
			goto EndWrite;
		}
/* write end Hunk_Reloc32 */
	LongToByteArr(bytes, 0);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_Reloc32 end\n", 30);
		status = FALSE;
		goto EndWrite;
	}
/* write Hunk_End */
	LongToByteArr(bytes, HUNK_END);
	if (Write(File, bytes, 4) != 4) {
		Write(Output(), "Write Error, Hunk_End\n", 22);
		status = FALSE;
		goto EndWrite;
	}

EndWrite:
	Close(File);
	FreeMem((UBYTE *)CodeSegment, CodeLen);
	for (temp = RelocList; temp; temp = next) {
		next = temp->Next;
		FreeMem((UBYTE *)temp, sizeof(struct RelocNode));
	}
	if (status) {
		SetProtection(filename, 0x02);
		ClearFlag(SF_Modified);
	}
	return(status);
}

/*
 * Make Code Segment
 *
 * Convert KeyMap to a HUNK_CODE hunk
 *
 * parameter:	KeyMapNode - Pointer to Key Map Node
 * returns:	LoadSeg - Pointer to LoadSeg
 */
ULONG MakeCodeSeg(KeyMapNode, filename)
struct KeyMapNode *KeyMapNode;
char *filename;
{
	UBYTE *Segment;
	struct KeyMap *KeyMap;
	ULONG	*NewKeyMap;
	struct HalfKeyMap *HalfKeyMap, *NewHalfKeyMap;
	USHORT	KeyCode;
	int	maphalf;
	ULONG	SegmentLen, len, offset;
	UBYTE	headerlen;

	len = sizeof(struct KeyMap_Hunk);
	HalfKeyMap = (struct HalfKeyMap *)&KeyMapNode->kn_KeyMap;
	maphalf = 0;
	KeyCode = 0x00;
	while (maphalf < 2)
		if (((maphalf == 0) && (KeyCode < 0x40)) || ((maphalf == 1) && (KeyCode < 0x38))) {
			switch (HalfKeyMap->KeyMapTypes[KeyCode] & (KCF_STRING|KCF_DEAD|KCF_NOP)) {
				case KCF_STRING:
					len += StringKeyLength(HalfKeyMap, KeyCode, &headerlen);
					break;	
				case KCF_DEAD:
					len += DeadKeyLength(HalfKeyMap, KeyCode, &headerlen);
			} /* switch */
			KeyCode += 1;
		}
		else {
			KeyCode = 0x00;
			HalfKeyMap = (struct HalfKeyMap *)&KeyMapNode->kn_KeyMap.km_HiKeyMapTypes;
			maphalf += 1;
		}
	len += strlen(filename) + 1;
	len = (len & 0x03)?((len & ~0x03)+4):len;

	if ((CodeSegment = (struct KeyMap_Hunk *)AllocMem(len, MEMF_CLEAR)) == NULL) {
		Write(Output(), "Memory Allocation Failed, MakeCodeSeg:CodeSegment\n", 50);
		return(NULL);
	}
	SegmentLen = (len>>2) - 2;

	CodeSegment->Hunk = HUNK_CODE;
	CodeSegment->Length = SegmentLen;
	Segment = (UBYTE *)&CodeSegment->kh_KeyMapNode;
	offset = sizeof(struct KeyMap_Hunk) - 8;
	memcpy(Segment, (UBYTE *)KeyMapNode, offset);

	KeyMap = &CodeSegment->kh_KeyMapNode.kn_KeyMap;
	KeyMap->km_LoKeyMapTypes = (UBYTE *)((ULONG)&CodeSegment->kh_LoKeyMapTypes - (ULONG)&CodeSegment->kh_KeyMapNode);
	if (!InsertRelocList((ULONG)&KeyMap->km_LoKeyMapTypes - (ULONG)&CodeSegment->kh_KeyMapNode))
		return(NULL);
	KeyMap->km_LoKeyMap = (ULONG *)((ULONG)&CodeSegment->kh_LoKeyMap - (ULONG)&CodeSegment->kh_KeyMapNode);
	if (!InsertRelocList((ULONG)&KeyMap->km_LoKeyMap - (ULONG)&CodeSegment->kh_KeyMapNode))
		return(NULL);
	KeyMap->km_LoCapsable = (UBYTE *)((ULONG)&CodeSegment->kh_LoCapsable - (ULONG)&CodeSegment->kh_KeyMapNode);
	if (!InsertRelocList((ULONG)&KeyMap->km_LoCapsable - (ULONG)&CodeSegment->kh_KeyMapNode))
		return(NULL);
	KeyMap->km_LoRepeatable = (UBYTE *)((ULONG)&CodeSegment->kh_LoRepeatable - (ULONG)&CodeSegment->kh_KeyMapNode);
	if (!InsertRelocList((ULONG)&KeyMap->km_LoRepeatable - (ULONG)&CodeSegment->kh_KeyMapNode))
		return(NULL);
	KeyMap->km_HiKeyMapTypes = (UBYTE *)((ULONG)&CodeSegment->kh_HiKeyMapTypes - (ULONG)&CodeSegment->kh_KeyMapNode);
	if (!InsertRelocList((ULONG)&KeyMap->km_HiKeyMapTypes - (ULONG)&CodeSegment->kh_KeyMapNode))
		return(NULL);
	KeyMap->km_HiKeyMap = (ULONG *)((ULONG)&CodeSegment->kh_HiKeyMap - (ULONG)&CodeSegment->kh_KeyMapNode);
	if (!InsertRelocList((ULONG)&KeyMap->km_HiKeyMap - (ULONG)&CodeSegment->kh_KeyMapNode))
		return(NULL);
	KeyMap->km_HiCapsable = (UBYTE *)((ULONG)&CodeSegment->kh_HiCapsable - (ULONG)&CodeSegment->kh_KeyMapNode);
	if (!InsertRelocList((ULONG)&KeyMap->km_HiCapsable - (ULONG)&CodeSegment->kh_KeyMapNode))
		return(NULL);
	KeyMap->km_HiRepeatable = (UBYTE *)((ULONG)&CodeSegment->kh_HiRepeatable - (ULONG)&CodeSegment->kh_KeyMapNode);
	if (!InsertRelocList((ULONG)&KeyMap->km_HiRepeatable - (ULONG)&CodeSegment->kh_KeyMapNode))
		return(NULL);

	HalfKeyMap = (struct HalfKeyMap *)&KeyMapNode->kn_KeyMap;
	NewKeyMap = CodeSegment->kh_LoKeyMap;
	NewHalfKeyMap = (struct HalfKeyMap *)&CodeSegment->kh_KeyMapNode.kn_KeyMap;
	maphalf = 0;
	KeyCode = 0x00;
	while (maphalf < 2)
		if (((maphalf == 0) && (KeyCode < 0x40)) || ((maphalf == 1) && (KeyCode < 0x38))) {
			switch (HalfKeyMap->KeyMapTypes[KeyCode] & (KCF_STRING|KCF_DEAD|KCF_NOP)) {
				case KCF_STRING:
					len = StringKeyLength(HalfKeyMap, KeyCode, &headerlen);
					len = CopyStringKey(HalfKeyMap, KeyCode, &Segment[offset], headerlen);
					NewKeyMap[KeyCode] = offset;
					if (!InsertRelocList((ULONG)&NewHalfKeyMap->KeyMap[KeyCode]))
						return(NULL);
					offset += len;
					break;	
				case KCF_DEAD:
					len = DeadKeyLength(HalfKeyMap, KeyCode, &headerlen);
					len = CopyDeadKey(HalfKeyMap, KeyCode, &Segment[offset], headerlen);
					NewKeyMap[KeyCode] = offset;
					if (!InsertRelocList((ULONG)&NewHalfKeyMap->KeyMap[KeyCode]))
						return(NULL);
					offset += len;
			} /* switch */
			KeyCode += 1;
		}
		else {
			KeyCode = 0x00;
			HalfKeyMap = (struct HalfKeyMap *)&KeyMapNode->kn_KeyMap.km_HiKeyMapTypes;
			NewKeyMap = CodeSegment->kh_HiKeyMap;
			NewHalfKeyMap = (struct HalfKeyMap *)&CodeSegment->kh_KeyMapNode.kn_KeyMap.km_HiKeyMapTypes;
			maphalf += 1;
		}
	len = strlen(filename) + 1;
	memcpy(&Segment[offset], filename, len);
	CodeSegment->kh_KeyMapNode.kn_Node.ln_Name = (UBYTE *)offset;
	if (!InsertRelocList((ULONG)&CodeSegment->kh_KeyMapNode.kn_Node.ln_Name - (ULONG)Segment))
		return(NULL);
	return((SegmentLen+2)<<2);
}

/*
 * Insert Reloc List
 *
 * parameter:	Offset - offset of pointer
 * return:	Success - TRUE if success
 */
int InsertRelocList(offset)
ULONG	offset;
{
	struct RelocNode *new, *temp, *prev;

	prev = NULL;
	for (temp = RelocList; (temp != NULL) && (temp->Offset > offset); temp = temp->Next)
		prev = temp;
	if ((new = (struct RelocNode *)AllocMem(sizeof(struct RelocNode), MEMF_CLEAR)) == NULL) {
		Write(Output(), "Memory Allocation Failed, InsertRelocList\n", 42);
		return(FALSE);
	}
	new->Offset = offset;
	new->Next = temp;
	if (prev == NULL)
		RelocList = new;
	else
		prev->Next = new;
	return(TRUE);
}
