// CopyToAdf.cpp : Defines the entry point for the console application.
//

#include<windows.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>

#include "adflib.h"

extern "C" {
#include "adf_util.h"
#include "adf_raw.h"
#include "adf_dir.h"
#include "adf_bitm.h"
};

void Help()
{
	printf("CopyToAdf V1.1 - Command Line ADF Archive Modifier.\n");
	printf("Written by Soren Hannibal/Lemon.\n");
	printf("\n");
	printf("COPYTOADF source destination [subfolder] [options]\n");
	printf("\n");
	printf("  source          specifies the file to be copied\n");
	printf("  destination     specifies the desination adf archive file\n");
	printf("  subfolder       specifies subfolder inside the adf\n");
	printf("\n");
	printf("  -bootblock      use the source as the bootblock instead of a regular file (and calculate checksum)\n");
	printf("  -startsector=x  copy file to sector X instead of to a regular file\n");
	printf("  -numsectors=x   how many sectors to write (default=1) - use together with -startsector\n");
	printf("  -markinbitmap   mark sectors as used in the disk bitmap - use together with -startsector\n");
	printf("  -new            create an empty adf named Source\n");
}
char filename[MAX_PATH] = "";
char* filenamenopath = filename;
char adfname[MAX_PATH] = "";
char subfolder[MAX_PATH] = "";
bool copyToBootBlock = false;
bool createNewFloppy = false;
long startSector = -1;
long numSectors = 1;
bool markInBitmap = false;
const int DiskImageSize = 880 * 1024;
char adfImageInternal[DiskImageSize + 1];
int main(int argc, char* argv[])
{
	if (argc>1 && (!strcmp(argv[1], "/?") || !strcmp(argv[1], "-?")))
	{
		Help();
		exit(0);
	}
	if (argc<3)
	{
		printf("Unexpected number of arguments for CopyToAdf.\n");
		exit(1);
	}
	strcpy_s(filename, argv[1]);

	//find the filename without the path
	filenamenopath = filename;
	while (strchr(filenamenopath, '\\'))
	{
		filenamenopath = strchr(filenamenopath, '\\') + 1;
	}

	strcpy_s(adfname, argv[2]);


	for (int curarg = 3; curarg<argc; curarg++)
	{
		if (argv[curarg][0] == '-')
		{
			if (!_stricmp(argv[curarg], "-bootblock"))
				copyToBootBlock = true;
			else if (!_stricmp(argv[curarg], "-new"))
				createNewFloppy = true;
			else if (!_memicmp(argv[curarg], "-startsector=", strlen("-startsector=")))
			{
				startSector = atoi(&argv[curarg][strlen("-startsector=")]);
				if (startSector == 0)
				{
					printf("unable to parse '%s'.\n", argv[curarg]);
					exit(1);
				}
			}
			else if (!_memicmp(argv[curarg], "-numsectors=", strlen("-numsectors=")))
			{
				numSectors = atoi(&argv[curarg][strlen("-numsectors=")]);
				if (numSectors == 0)
				{
					printf("unable to parse '%s'.\n", argv[curarg]);
					exit(1);
				}
			}
			else if (!_stricmp(argv[curarg], "-markinbitmap"))
			{
				markInBitmap = true;
			}
			else
			{
				printf("unknown argument '%s'.\n", argv[curarg]);
				exit(1);
			}
		}
		else
			strcpy_s(subfolder, argv[curarg]);
	}



	bool needsADFLib = true;
	if (copyToBootBlock == true)
	{
		needsADFLib = false;
		printf("Adding bootblock %s%s%s to %s\n", subfolder, subfolder[0] ? "/" : "", filenamenopath, adfname);
	}
	else if (createNewFloppy)
	{
		printf("Creating empty floppy %s with name %s\n", adfname, filenamenopath);
	}
	else
	{
		printf("Adding file %s%s%s to %s\n", subfolder, subfolder[0] ? "/" : "", filenamenopath, adfname);
	}


	/* initialize the library */
	struct Device *dev = 0;
	struct Volume *vol = 0;
	if (needsADFLib)
	{
		adfEnvInitDefault();
		if (createNewFloppy)
		{
			dev = adfCreateDumpDevice(adfname, 80, 2, 11);
			if (!dev)
			{
				printf("Can't create the floppy '%s'.\n", adfname);
				adfEnvCleanUp(); exit(1);
			}

			/* create the filesystem : OFS with DIRCACHE */
			RETCODE rc = adfCreateFlop(dev, filenamenopath, FSMASK_DIRCACHE);
			if (rc != RC_OK)
			{
				printf("Can't create the floppy '%s'.\n", adfname);
				adfEnvCleanUp(); exit(1);
			}
		}
		else
		{
			dev = adfMountDev(adfname, FALSE);
			if (!dev)
			{
				printf("Can't mount the floppy '%s'.\n", adfname);
				adfEnvCleanUp(); exit(1);
			}
		}

		vol = adfMount(dev, 0, FALSE);
		if (!vol)
		{
			adfUnMountDev(dev);
			printf("Can't mount the volume\n");
			exit(1);
		}
		if (subfolder[0] != 0)
		{
			if (adfChangeDir(vol, subfolder) == RC_ERROR)
			{
				//folder doesn't exist - create it
				if (adfCreateDir(vol, vol->curDirPtr, subfolder) == RC_ERROR)
				{
					printf("Can't create subfolder\n");
					exit(1);
				}
				if (adfChangeDir(vol, subfolder) == RC_ERROR)
				{
					printf("Can't switch to subfolder after creating it\n");
					exit(1);
				}
			}
		}
	}
	else
	{
		FILE* handle = fopen(adfname, "rb");
		if (!handle)
		{
			printf("Couldn't open file %s\n", adfname);
			exit(1);
		}
		int n = fread(adfImageInternal, sizeof(unsigned char), DiskImageSize + 1, handle);
		if (n != DiskImageSize)
		{
			printf("File %s is not 880kb large, and is therefore not a regular ADF disk image\n", adfname);
			exit(1);
		}
		fclose(handle);

	}



	if (!createNewFloppy)
	{
		struct File* file;
		FILE* in;

		/* a device and a volume 'vol' has been successfully mounted */


		in = fopen(filename, "rb");
		if (!in)
		{
			printf("Couldn't open file %s\n", filename);
			exit(1);
		}
		if (!copyToBootBlock && startSector < 0)
		{
			struct bEntryBlock dirblock;
			adfReadEntryBlock(vol, vol->curDirPtr, &dirblock);
			struct bEntryBlock entry;
			SECTNUM nSect = adfNameToEntryBlk(vol, dirblock.hashTable, filenamenopath, &entry, NULL);
			if (nSect != -1)
			{
				if (adfRemoveEntry(vol, vol->curDirPtr, filenamenopath) != RC_OK)
				{
					printf("Failed to delete existing file %s%s%s in adf\n", subfolder, subfolder[0] ? "/" : "", filenamenopath);
					exit(0);
				}
			}
			file = adfOpenFile(vol, filenamenopath, "w");
			if (!file)
			{
				printf("Failed to create file %s%s%s in adf\n", subfolder, subfolder[0] ? "/" : "", filenamenopath);
				exit(1);
			}
		}
#define BUFSIZE 1000000
		unsigned char buf[BUFSIZE];
		memset(buf, 0, BUFSIZE);
		int n = fread(buf, sizeof(unsigned char), BUFSIZE, in);
		if (n == BUFSIZE)
		{
			printf("File %s is too large for adf\n", filename);
			exit(1);
		}
		if (n > 1024 && copyToBootBlock)
		{
			printf("File %s is larger than 1024 - will not fit in bootblock\n", filename);
			exit(1);
		}

		if (n > (numSectors * 512) && startSector >= 0)
		{
			printf("File %s is %d sectors large - will not fit in %d sectors\n", filename, (n + 511) / 512, numSectors);
			exit(1);
		}
		if (copyToBootBlock)
		{
			unsigned long newSum = adfBootSum(buf);
			swLong(buf + 4, newSum);
			memcpy(adfImageInternal, buf, 1024);
		}
		else if ((numSectors >= 0 && startSector >= 0))
		{

			for (int i = 0; i < numSectors; i++)
			{
				if (adfWriteBlock(vol, startSector + i, buf + i * 512) != RC_OK)
				{
					printf("Failed to write sector %d to adf\n", startSector + i);
					exit(1);
				}
				if (markInBitmap)
				{
					adfSetBlockUsed(vol, startSector + i);
				}
			}
			if (markInBitmap)
			{
				if (adfUpdateBitmap(vol) != RC_OK)
				{
					printf("Failed to update bitmap in adf\n");
					exit(1);
				}
			}

		}
		else
		{
			int bytesWritten = adfWriteFile(file, n, buf);
			if (bytesWritten != n)
			{
				printf("Failed to copy file %s%s%s to adf\n", subfolder, subfolder[0] ? "/" : "", filenamenopath);
				exit(1);
			}
			adfCloseFile(file);
		}
		fclose(in);
	}


	if (needsADFLib)
	{
		adfUnMount(vol);
		adfUnMountDev(dev);
		adfEnvCleanUp();
	}
	else
	{
		FILE* handle = fopen(adfname, "wb");
		if (!handle)
		{
			printf("Couldn't open file %s for writing\n", adfname);
			exit(1);
		}
		int n = fwrite(adfImageInternal, sizeof(unsigned char), DiskImageSize, handle);
		fclose(handle);
		if (n != DiskImageSize)
		{
			printf("Could not write to %s\n", adfname);
			exit(1);
		}
	}

	return(0);
}
