// DO   = Matthias Domin (Matthias.Domin@t-online.de)
// 42BS = 42Bastian Schick (b.schick@enea.de)


// June 5, 1999: DO ported from 68k-assembler

// #define DEBUG

#define VER "5.0"

#define MAXFILE 256

#define CARDHEADLEN 0x0408
#define MAXCARDMEM (256*BLOCKSIZE)-CARDHEADLEN
#define CARDSIZE 10
#define BLOCKSIZE (1<<CARDSIZE)
#define HEADERLEN 10


#include "lynxer.h"

int verbose;


typedef struct {
	unsigned char d_attrib[1];
	unsigned char d_time[2];
	unsigned char d_date[2];
	long d_length;
	unsigned char d_fname[14];
} DTA, *pDTA;

DTA dta;

//unsigned char CardHead[CARDHEADLEN];		// 1032
#include "loader26.h"


char AccessPath[_MAX_PATH];
char MAKPath[_MAX_PATH];
char LYXPath[_MAX_PATH];

char Drive[_MAX_DRIVE];		// drive letter followed by a colon (:)
char Path[_MAX_PATH];		// directory path including trailing slash
char FileName[_MAX_FNAME];	// Base filename ( no extension)
char Extension[_MAX_EXT];	// filename extension including leading period (.)

char OldDrive[_MAX_DRIVE];	// drive letter followed by a colon (:)
char OldPath[_MAX_PATH];
char OldExtension[_MAX_EXT];// filename extension including leading period (.)


char PathArray[MAXFILE][_MAX_PATH];

long FileLengths[MAXFILE];

unsigned char *pCardMem;
long nCardLen;
long nCardOffset;

unsigned char MAKbuffer[1024];
unsigned char crctab[256];
unsigned char BuildLYX[256*BLOCKSIZE];

/*************************************************************
*** error                                                  ***
*************************************************************/
void error(int line,char *f,...)
{
  va_list argp;
  
  va_start(argp,f);
  printf("Error (%d):",line);
  printf(f,va_arg(argp,char *) );
  va_end(argp);
#ifdef ATARI
  while (getc(stdin) != ' ');
#endif
  exit(-1);
}

/*************************************************************
*** InitBuffer                                             ***
*************************************************************/
void InitBuffer()
{
	int i;
	for (i=0; i<256*1024; i++)
		BuildLYX[i] = -1;
}

/*************************************************************
*** Check                                                  ***
*** Builds a checksum and puts it at the end of the image  ***
*************************************************************/
void Check()
{
	int i, len;
	unsigned char *cp;
	unsigned char ch, d1, d2, d2old;


// check1:    Initializing crctab
	ch = 0;
	do
	{
// check2:
		d2 = ch;
		for (len = 7; len >= 0; len--)
		{
			d2old = d2;
			d2 <<= 1;
			if (d2old & 0x80)
				d2 ^= 0x95;
		}
		crctab[ch] = d2;
	}
	while(--ch);



// loopCheck:
	cp = BuildLYX;
	len  = nCardLen;
	d1 = 0;
	while (len > 0)
	{
		ch = *cp++;
		ch = crctab[ch];
		d1 ^= ch;
		len--;
	}

// loopCheck1:
	for (i=255; i>=0; i--)
	{
		ch = crctab[i];
		ch ^= d1;
		if (ch == '\0')
			break;
	}
// ok_check:
	*cp = i;
}


/*************************************************************
*** ToUpper                                                ***
*** converts a string to upper chars                       ***
*** string ends at 0, CR, ';' or '*'                       ***
*************************************************************/
int ToUpper(char **pSource /* a5 */, char *pDest /* a0 */)
{

	int nCount;
	char *pSrc = *pSource;
	char ch;


	if (*pSrc == '\0')	// Something there?
		goto EOF_TU;	// no!

	nCount = 0;

ToUpper2:
	if ((ch = *pSrc++) == '\0')
		goto EOL_TU0;

	if (ch == ' ')
		goto ToUpper2;

	if (ch == '\t')
		goto ToUpper2;

	if (ch == '\r')
		goto EOL_TU1;

	// comment
	if (ch == '*' || ch == ';')
	{
comment:
		if ((ch = *pSrc++) == '\0')
			goto EOL_TU0;
		if (ch == '\r')
			goto EOL_TU1;
		goto comment;
	}

// no_comment:
	*pDest++ = toupper(ch);
	nCount++;
	goto ToUpper2;			// loop



EOL_TU0:
	--pSrc;
	*pSource = pSrc;
	*pDest = 0;
	return nCount;

EOL_TU1:
	++pSrc;
	*pSource = pSrc;
	*pDest = 0;
	return nCount;

EOF_TU:
	return -1;
}

/*************************************************************
*** LoadFile                                               ***
*************************************************************/
unsigned long LoadFile(char *fn,unsigned char* ptr)
{
	unsigned long len;
	int f;

	if ((f = open(fn,O_RDONLY | O_BINARY)) >= 0)
	{
		len = lseek(f,0L,SEEK_END);
		lseek(f,0L,SEEK_SET);

#ifdef DEBUG
		printf("filesize: %lu\n", len);
#endif
		len  = read(f,ptr,len);
#ifdef DEBUG
//		printf("sizeof(int): %u", sizeof(int));
		printf("bytes read: %lu\n", len);
#endif
		close(f);
		if (verbose)
			printf("Read: %s \n",fn);

		return (len);
	}
	else
		return 0;
}


/*************************************************************
*** SaveFile                                               ***
*************************************************************/
void SaveFile(char *filename,unsigned char *ptr, long size)
{
	int handle;
   
	if ( (handle = open(filename,O_CREAT | O_TRUNC | O_BINARY | O_RDWR, 0644)) <0 )
	{
		error(__LINE__,"Couldn't open %s for writing !\n",filename);
	}
	if ( write(handle,ptr,size) != size )
		printf("Error: Couldn't write %s !\n",filename);
	else
		if (verbose)
			printf("Written: %s \n"
					"--------------------------\n",filename);
 
	close(handle);
}

/*************************************************************
*** SaveLoader                                             ***
*** Helper function: Transforms a binary into a *.H-file   ***
*************************************************************/
void SaveLoader(char *filename,unsigned char *ptr, long size)
{
	int handle;
	char buffer[256];
	int i;
	   
	if ( (handle = open(filename,O_CREAT | O_TRUNC | O_TEXT | O_RDWR, 0644)) <0 )
	{
		error(__LINE__,"Couldn't open %s for writing !\n",filename);
	}

	sprintf(buffer, "/****************************************\n");
	write(handle,buffer,strlen(buffer));
	sprintf(buffer, "*** LOADER26.H                       ***\n");
	write(handle,buffer,strlen(buffer));
	sprintf(buffer, "***************************************/\n\n");
	write(handle,buffer,strlen(buffer));


	sprintf(buffer, "unsigned char CardHead[1032] = {\n");
	write(handle,buffer,strlen(buffer));
	while (size > 8)
	{
		sprintf(buffer, "\t0x%x, 0x%x, 0x%x, 0x%x,  0x%x, 0x%x, 0x%x, 0x%x,\n",
			*(ptr+0), *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5), *(ptr+6), *(ptr+7));

		write(handle,buffer,strlen(buffer));
		ptr += 8;
		size -= 8;
	}

	buffer[0] = '\0';
	sprintf(buffer, "\t");
	write(handle,buffer,strlen(buffer));

	for (i=0; i < size-1; i++)
	{
		sprintf(buffer, "0x%x, ", *ptr++);
		write(handle,buffer,strlen(buffer));
	}
	sprintf(buffer, "0x%x\n", *ptr);
	write(handle,buffer,strlen(buffer));

	sprintf(buffer, "\t};\n");
	write(handle,buffer,strlen(buffer));

	close(handle);
}

/*************************************************************
*** Fsfirst                                                ***
*** Returns the filelength                                 ***
*************************************************************/
unsigned long Fsfirst(char *fn)
{
	unsigned long len;
	int f;

	if ((f = open(fn,O_RDONLY | O_BINARY)) >= 0)
	{
		len = lseek(f,0L,SEEK_END);

		close(f);
		if (verbose)
			printf("File length: %s=%d \n",fn, len);

		return (len);
	}
	else
		return 0;
}


/*************************************************************
*** Main                                                   ***
*************************************************************/
int main(int argc,char *argv[])
{
	int i;
	int nLen;
	unsigned char *cp;
	unsigned char *fp;
	unsigned char *pMB;
	unsigned char fflag;

	int nErr;
	int nFileCount;
	long nSum;

	int nBlockNo, nBlockOffset, nTmpBlockOffset, nStart;
	int nTmpFileLength, nTmpFileLength2;

	/* batch */
	FILE *batch_handle = NULL;


	printf("------------------------------\n"
         "Lynxer Version "VER"\n"
         "(c) 1996-1999 42Bastian Schick \n"
         "              Matthias  Domin  \n"
         "------------------------------\n");

	if (argc == 1 )
	{
		printf(argv[0]);
		printf("\n");
		printf("Usage :\n"
		"lynxer homebrew.o\n"
		"or\n"
		"lynxer batchfile\n" );
		exit(0);
	}

	--argc;

/*
    The next lines were used to transform a binary file
	into a *.H-file!

	printf("Loading Card header file LOADER26.O\n");
	if (LoadFile("LOADER26.O", CardHead) <= 0)
	{
		printf("LOADER26.O not found!");
		exit(EXIT_FAILURE);
	}
	SaveLoader("loader26.h", CardHead, CARDHEADLEN);
	exit(0);
*/
	InitBuffer();

	// The path from which LYNXER is called
	_splitpath(argv[0], OldDrive, OldPath, FileName, Extension);
#ifdef DEBUG
	printf(OldDrive);
	printf("\n");
	printf(OldPath);
	printf("\n");
	printf(FileName);
	printf("\n");
	printf(Extension);
	printf("\n");
#endif

	// Path of the first commandline argument (Make-file or *.O-file)
	_splitpath(argv[1], Drive, Path, FileName, Extension);
	// Path of the result of LYNXERs work
	_makepath(LYXPath, Drive, Path, FileName, ".LYX");

	if (strlen(Extension) == 0 ||
		!stricmp(Extension, ".OBJ") ||
		!stricmp(Extension, ".LYX") ||		// ?????
		!stricmp(Extension, ".O"))
	{
		strcpy(OldExtension, Extension);
		strcpy(Extension, ".MAK");
	}

	if (strlen(FileName) == 0)
		strcpy(FileName, "MAKE_ROM");

	if (strlen(Path) == 0)
		strcpy(Path, OldPath);

	if (strlen(Drive) == 0)
		strcpy(Drive, OldDrive);

#ifdef DEBUG
	printf("****Zur Auswertung kommt (in Teilen):\n");
	printf(Drive);
	printf("\n");
	printf(Path);
	printf("\n");
	printf(FileName);
	printf("\n");
	printf(Extension);
	printf("\n");
#endif

	_makepath(MAKPath, Drive, Path, FileName, Extension);
#ifdef DEBUG
	printf("****Zur Auswertung kommt (zusammen):\n");
	printf(MAKPath);
	printf("\n");
#endif


	// Nun lesen wir entweder eine MAK-Datei nach MAKbuffer ein,
	if (LoadFile(MAKPath, MAKbuffer) <= 0)
	{	//  oder erzeugen uns dort selbst eine
#ifdef DEBUG
		printf("Generating internal MAK-file:\n");
#endif	
		// Zunächst "INSERT.O" als Titlesprite
		_makepath(MAKbuffer,  OldDrive, OldPath, "INSERT", ".O");
		cp = MAKbuffer + strlen(MAKbuffer);
		*cp++ = '\r';	// Carriage return und
		*cp++ = '\n';	// Linefeed anhängen
		_makepath(cp, Drive, Path, FileName, OldExtension);
		
	}
#ifdef DEBUG
	printf(MAKbuffer);
	printf("\n");

	printf("Parsen der MAK-Datei ergibt:\n");
#endif	

// Parsen der MAKbuffer-Liste und übertragen in das PathArray
	nFileCount = 0;
	pMB = MAKbuffer;
	while ((nLen = ToUpper(&pMB, AccessPath)) > 0)
	{
		if (nLen > 0)
		{
			strcpy(PathArray[nFileCount++], AccessPath);
			printf("%s\n", AccessPath);
		}

		if (nFileCount == MAXFILE)
			break;
	}


// Makedatei wurde in interne Dateiliste überführt
	nErr = 0;
	for (i=0; i<nFileCount; i++)
	{
		_splitpath(PathArray[i], Drive, Path, FileName, Extension);
		if (!stricmp(FileName, "#ALIGN"))
		{
			// Align-Anweisung
			FileLengths[i] = 0;
		}
		else	// Dateiangabe
		{
			if (!strlen(Extension))
			{
				strcpy(Extension, ".O");
			}
			_makepath(PathArray[i], Drive, Path, FileName, Extension);
			if ((nLen = Fsfirst(PathArray[i])) <= 0)
			{
				nErr = -1;
				printf("File not found: %s\n", PathArray[i]);
			}
			FileLengths[i] = ((nLen + 1) & 0xfffffffe);
#ifdef DEBUG
			printf("%d ==> %d\n", nLen, FileLenghts[i]);
#endif	

		}
	} // for

	if (nErr == -1)
		error(__LINE__, "Unable to load all files!");


	nSum = 0;
	for (i=0; i<nFileCount; i++)
	{
		nSum += FileLengths[i];
	}
#ifdef DEBUG
			printf("==> Image size: %d\n", nSum);
#endif	

	if (nSum > MAXCARDMEM)
		error(__LINE__, "IMAGE size exceeds ROM size!");


	
// Jetzt geht's los:
// Zunächst den Header
	nCardLen = nSum;
	cp = BuildLYX;
	pCardMem = BuildLYX + 0x0380;

	for (i=0; i< CARDHEADLEN; i++) // 0x0408
		*cp++ = CardHead[i];

	nCardOffset = (nFileCount + 1) * 8 + 0x0380;
	if (nCardOffset < CARDHEADLEN)	// ??????????????????????
		nCardOffset = CARDHEADLEN;

	nCardLen += nCardOffset;

	cp = BuildLYX + nCardOffset;  // cp = a5 --> Ab hier wird abgelegt

	nBlockOffset = nCardOffset % BLOCKSIZE;
	nBlockNo = nCardOffset / BLOCKSIZE;

#ifdef DEBUG
	printf("nCardLen: %d = %xh\n", nCardLen, nCardLen);
	printf("nCardOffset: %d = %xh\n", nCardOffset, nCardOffset);
	printf("Block %xh, Offset %xh\n", nBlockNo, nBlockOffset);
	printf("*********************************************\n");
#endif	


// loop7
	for (i=0; i < nFileCount; i++)
	{
#ifdef DEBUG
			printf("%d. entry in list ----------------------------\n", i+1);
#endif	

		if (FileLengths[i] == 0)	// Dann Block-Align durchführen
		{
			if (nBlockOffset != 0)
			{
				nBlockNo += 1;
				cp += ((1<<CARDSIZE) - nBlockOffset);
				nCardLen += ((1<<CARDSIZE) - nBlockOffset);
				nBlockOffset = 0;
			}
#ifdef DEBUG
			printf("#ALIGN:\n");
			printf("Next one will be at: Block %x, Offset %x\n", nBlockNo, nBlockOffset);
#endif	
		}
		else	// sonst war es eine Datei
		{
			fp = cp;
			LoadFile(PathArray[i], fp);
			nTmpFileLength = FileLengths[i];
			nTmpFileLength2 = FileLengths[i];
			cp += nTmpFileLength;
			*(pCardMem + 0) = nBlockNo;
			*(pCardMem + 1) = ((nBlockOffset ^ 0xffff) & 0xff);
			*(pCardMem + 2) = ((nBlockOffset ^ 0xffff) >> 8);
			nStart = 0;
			fflag = *fp | *(fp+1);
			if ((fflag == 0x88) ||	// Normales Programm
				(fflag == 0x89))	// Gepacktes Programm
			{
				// Korrigierten Offset
				nTmpBlockOffset = nBlockOffset + HEADERLEN;  // und die BlockNo ???
				*(pCardMem + 1) = ((nTmpBlockOffset ^ 0xffff) & 0xff);
				*(pCardMem + 2) = ((nTmpBlockOffset ^ 0xffff) >> 8);
				*(pCardMem + 3) = fflag;
				nStart = (*(fp+2))*256 + *(fp+3);
				nTmpFileLength -= HEADERLEN;

				if (fflag == 0x89)	// Gepacktes Prg.
				{
					nTmpFileLength = (*(fp+4))*256 + *(fp+5);
				}

			}
// noPrg
			*(pCardMem + 4) = nStart & 0xff;	// Steht "falsch" rum drin
			*(pCardMem + 5) = nStart >> 8;
			*(pCardMem + 6) = ((nTmpFileLength ^ 0xffff) & 0xff);
			*(pCardMem + 7) = ((nTmpFileLength ^ 0xffff) >> 8);

			nBlockNo += nTmpFileLength2 / BLOCKSIZE;
			nBlockOffset += nTmpFileLength2 % BLOCKSIZE;
			while(nBlockOffset >= BLOCKSIZE)
			{
				nBlockNo += 1;
				nBlockOffset -= BLOCKSIZE;
			}
			pCardMem += 8;
#ifdef DEBUG
			printf("File handled:\n");
			printf("Next one will be at:  %x, Offset %x\n", nBlockNo, nBlockOffset);
#endif	
		}

	} // for 

	Check();	// 
	BuildLYX[0x303] = 0x80; // Patch: BRA instead of BEQ --> No checksum required
	SaveFile(LYXPath, BuildLYX, nCardLen);

#ifdef ATARI
	if (verbose)  getc(stdin);
#endif
  return 0;
}
