/** fMSX: portable MSX emulator ******************************/
/**                                                         **/
/**                          Disk.c                         **/
/**                                                         **/
/** This file contains standard disk access drivers working **/
/** with disk images from files.                            **/
/**                                                         **/
/** Copyright (C) Marat Fayzullin 1994-2007                 **/
/**     You are not allowed to distribute this software     **/
/**     commercially. Please, notify me, if you make any    **/
/**     changes to this file.                               **/
/*************************************************************/
#ifdef DISK

#include "MSX.h"
#include "Floppy.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

#ifdef MSDOS
#include <io.h>
#endif

#ifdef WINDOWS
#include <io.h>
#endif

#if defined(UNIX) || defined(S60) || defined(PSP)
#include <unistd.h>
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif

static int Drives[2] = { -1,-1 };      /* Disk image handles */
static byte *Images[2] = { 0,0 };      /* Disk image buffers */
static int RdOnly[2];                  /* 1 = read-only      */ 

/** DiskPresent() ********************************************/
/** Return 1 if disk drive with a given ID is present.      **/
/*************************************************************/
byte DiskPresent(byte ID)
{
  return((ID<MAXDRIVES)&&((Drives[ID]>=0)||Images[ID]));
}

/** DiskRead() ***********************************************/
/** Read requested sector from the drive into a buffer.     **/
/*************************************************************/
byte DiskRead(byte ID,byte *Buf,int N)
{
  if(ID<MAXDRIVES)
  {
    if(Images[ID]&&(N>=0)&&(N<DSK_SECS_PER_DISK))
    { memcpy(Buf,Images[ID]+N*512,512);return(1); }

    if(Drives[ID]>=0)
      if(lseek(Drives[ID],N*512L,0)==N*512L)
        return(read(Drives[ID],Buf,512)==512);
  }

  return(0);
}

/** DiskWrite() **********************************************/
/** Write contents of the buffer into a given sector of the **/
/** disk.                                                   **/
/*************************************************************/
byte DiskWrite(byte ID,const byte *Buf,int N)
{
  if((ID<MAXDRIVES)&&!RdOnly[ID])
  {
    if(Images[ID]&&(N>=0)&&(N<DSK_SECS_PER_DISK))
    { memcpy(Images[ID]+N*512,Buf,512);return(1); }

    if(Drives[ID]>=0)
      if(lseek(Drives[ID],N*512L,0)==N*512L)
        return(write(Drives[ID],Buf,512)==512);
  }

  return(0);
}

/** ChangeDisk() *********************************************/
/** Change disk image in a given drive. Closes current disk **/
/** image if Name=0 was given. Returns 1 on success or 0 on **/
/** failure.                                                **/
/*************************************************************/
byte ChangeDisk(byte ID,const char *Name)
{
  byte Magic[8];

  /* We only have MAXDRIVES drives */
  if(ID>=MAXDRIVES) return(0);

  /* Close previous disk image */
  if(Drives[ID]>=0) { close(Drives[ID]);Drives[ID]=-1; }
  if(Images[ID])    { free(Images[ID]);Images[ID]=0; }

  /* If no disk image given, consider drive empty */
  if(!Name) return(1);

  /* Open new disk image */
  Drives[ID]=open(Name,O_RDWR|O_BINARY);
  RdOnly[ID]=0;

  /* If failed to open for writing, open read-only */
  if(Drives[ID]<0)
  {
    Drives[ID]=open(Name,O_RDONLY|O_BINARY);
    RdOnly[ID]=1;
  }

  /* Verify that open file is really a disk image */
  if(Drives[ID]>=0)
    if((read(Drives[ID],Magic,1)!=1)||(Magic[0]!=0xEB))
    { close(Drives[ID]);RdOnly[ID]=0;Drives[ID]=-1; }

  /* If failed to open as a plain file, */
  /* try creating disk image in memory  */
  if(Drives[ID]<0) Images[ID]=DSKLoad(Name,0);
  if(Images[ID]) RdOnly[ID]=0;

  /* Return operation result */
  return(Images[ID]||(Drives[ID]>=0));
}

#endif /* DISK */
