/*
  Hatari - dim.c

  This file is distributed under the GNU General Public License, version 2
  or at your option any later version. Read the file gpl.txt for details.

  DIM disk image support.
*/
const char DIM_fileid[] = "Hatari dim.c";

#include "main.h"
#if HAVE_ZLIB_H
#include <zlib.h>
#endif

#include "file.h"
#include "floppy.h"
#include "dim.h"

#undef SAVE_TO_DIM_IMAGES

/*
    .DIM FILE FORMAT
  --===============-------------------------------------------------------------

  The file format of normal .DIM image files are quite the same as the .ST image
  files (see st.c) - the .DIM image files just have an additional header of
  32 bytes. However, there are also "compressed" images which only contain the
  used sectors of the disk. It is necessary to parse the FAT to "uncompress"
  these images.

  The header contains following information:

  Offset  Size      Description
  ------  --------  -----------
  0x0000  Word      ID Header (0x4242('BB'))
  0x0002  Byte      1 = disk configuration has been detected automatically
                    0 = the user specified the disk configuration
  0x0003  Byte      Image contains all sectors (0) or only used sectors (1)
  0x0006  Byte      Sides (0 or 1; add 1 to this to get correct number of sides)
  0x0008  Byte      Sectors per track
  0x000A  Byte      Starting Track (0 based)
  0x000C  Byte      Ending Track (0 based)
  0x000D  Byte      Double-Density(0) or High-Density (1)
  0x000E  18 Bytes  A copy of the Bios Parameter Block (BPB) of this disk.
*/


/*-----------------------------------------------------------------------*/
/**
 * Does filename end with a .DIM extension? If so, return TRUE
 */
bool DIM_FileNameIsDIM(const char *pszFileName, bool bAllowGZ)
{
	return(File_DoesFileExtensionMatch(pszFileName,".dim")
	       || (bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".dim.gz")));
}


/*-----------------------------------------------------------------------*/
/**
 * Load .DIM file into memory, set number of bytes loaded and return a pointer
 * to the buffer.
 */
uint8_t *DIM_ReadDisk(int Drive, const char *pszFileName, long *pImageSize, int *pImageType)
{
	uint8_t *pDimFile;
	uint8_t *pDiskBuffer = NULL;

	/* Load file into buffer */
	pDimFile = File_Read(pszFileName, pImageSize, NULL);
	if (pDimFile)
	{
		/* Check header for valid image: */
		if (pDimFile[0x00] != 0x42 || pDimFile[0x01] != 0x42 ||
		    pDimFile[0x03] != 0 || pDimFile[0x0A] != 0)
		{
			fprintf(stderr, "This is not a valid DIM image!\n");
			*pImageSize = 0;
			free(pDimFile);
			return NULL;
		}

		/* Simply use disk contents without the DIM header: */
		*pImageSize -= 32;
		pDiskBuffer = malloc(*pImageSize);
		if (pDiskBuffer)
			memcpy(pDiskBuffer, pDimFile+32, *pImageSize);
		else
			perror("DIM_ReadDisk");

		/* Free DIM file we loaded */
		free(pDimFile);
	}

	if (pDiskBuffer == NULL)
	{
		*pImageSize = 0;
		return NULL;
	}

	*pImageType = FLOPPY_IMAGE_TYPE_DIM;
	return pDiskBuffer;
}


/*-----------------------------------------------------------------------*/
/**
 * Save .DIM file from memory buffer. Returns TRUE is all OK
 */
bool DIM_WriteDisk(int Drive, const char *pszFileName, uint8_t *pBuffer, int ImageSize)
{
#ifdef SAVE_TO_DIM_IMAGES

	unsigned short int nSectorsPerTrack, nSides;
	uint8_t *pDimFile;
	int nTracks;
	bool bRet;
#if HAVE_LIBZ
	gzFile hGzFile;
#else
	FILE *fhdl;
#endif

	/* Allocate memory for the whole DIM image: */
	pDimFile = malloc(ImageSize + 32);
	if (!pDimFile)
	{
		perror("DIM_WriteDisk");
		return false;
	}

	memset(pDimFile, 0, 32);
	/* Try to load the old header data to preserve the header fields that are unknown yet: */
#if HAVE_LIBZ
	hGzFile = gzopen(pszFileName, "rb");
	if (hGzFile != NULL)
	{
		gzread(hGzFile, pDimFile, 32);
		gzclose(hGzFile);
	}
#else
	fhdl = fopen(pszFileName, "rb");
	if (fhndl != NULL)
	{
		fread(pDimFile, 32, 1, fhndl);
		fclose(fhndl);
	}
#endif

	/* Now fill in the new header information: */
	Floppy_FindDiskDetails(pBuffer, ImageSize, &nSectorsPerTrack, &nSides);
	nTracks = ((ImageSize / NUMBYTESPERSECTOR) / nSectorsPerTrack) / nSides;
	pDimFile[0x00] = pDimFile[0x01] = 0x42;     /* ID */
	pDimFile[0x03] = 0;                         /* Image contains all sectors */
	pDimFile[0x06] = nSides - 1;                /* Sides */
	pDimFile[0x08] = nSectorsPerTrack;          /* Sectors per track */
	pDimFile[0x0A] = 0;                         /* Starting track */
	pDimFile[0x0C] = nTracks - 1;               /* Ending track */
	pDimFile[0x0D] = (ImageSize > 1024*1024);   /* DD / HD flag */

	/* Now copy the disk data: */
	memcpy(pDimFile + 32, pBuffer, ImageSize);
	
	/* And finally save it: */
	bRet = File_Save(pszFileName, pDimFile, ImageSize + 32, false);

	free(pDimFile);

	return bRet;

#else   /*SAVE_TO_ST_IMAGES*/

	/* Oops, cannot save */
	return false;

#endif  /*SAVE_TO_ST_IMAGES*/
}
