/* disk.h: Routines for handling disk images
   Copyright (c) 2007-2023 Gergely Szasz

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

   Author contact information:

   Philip: philip-fuse@shadowmagic.org.uk

*/

#ifndef FUSE_DISK_H
#define FUSE_DISK_H

static const unsigned int DISK_FLAG_NONE = 0x00;
static const unsigned int DISK_FLAG_PLUS3_CPC = 0x01;	/* try to fix some CPC issue */
static const unsigned int DISK_FLAG_OPEN_DS = 0x02;	/* try to open the other side too */

typedef enum disk_error_t {
  DISK_OK = 0,
  DISK_IMPL,
  DISK_MEM,
  DISK_GEOM,
  DISK_OPEN,
  DISK_UNSUP,
  DISK_RDONLY,
  DISK_CLOSE,
  DISK_WRFILE,
  DISK_WRPART,

  DISK_LAST_ERROR,
} disk_error_t;

typedef enum disk_type_t {
  DISK_TYPE_NONE = 0,
  DISK_UDI,		/* raw track disk image (our format :-) */
  DISK_FDI,		/* Full Disk Image ALT */
  DISK_TD0,

  /* DISCiPLE / +D / SAM Coupe */
  DISK_MGT,		/* ALT */
  DISK_IMG,		/* OUT-OUT */
  DISK_SAD,		/* OUT-OUT with geometry header */

  DISK_CPC,
  DISK_ECPC,

  /* Beta disk interface (TR-DOS) */
  DISK_TRD,
  DISK_SCL,

  /* Opus Discovery */
  DISK_OPD,

  /* Didaktik 40/80 */
  DISK_D40,
  DISK_D80,

  /* Log disk structure (.log) */
  DISK_LOG,

  DISK_TYPE_LAST,
} disk_type_t;

typedef enum disk_dens_t {
  DISK_DENS_AUTO = 0,
  DISK_8_SD,		/* 8" SD floppy 5208 MF */
  DISK_8_DD,		/* 8" DD floppy 10416 */
  DISK_SD,		/* 3125 bpt MF */
  DISK_DD,		/* 6250 bpt */
  DISK_DD_PLUS,		/* 6500 bpt e.g. Coin Op Hits */
  DISK_HD,		/* 12500 bpt*/
} disk_dens_t;

typedef struct disk_t {
  char *filename;	/* original filename */
  int sides;		/* 1 or 2 */
  int cylinders;	/* tracks per side  */
  int bpt;		/* bytes per track */
  int wrprot;		/* disk write protect */
  int dirty;		/* disk changed */
  int have_weak;	/* disk contain weak sectors */
  unsigned int flag;
  disk_error_t status;		/* last error code */
  libspectrum_byte *data;	/* disk data */
/* private part */
  int tlen;			/* length of a track with clock and other marks (bpt + 3/8bpt) */
  int c_bpt;			/* current track bpt value it can be other than allocated bpt */
  libspectrum_byte *track;	/* current track data bytes */
  libspectrum_byte *clocks;	/* clock marks bits */
  libspectrum_byte *fm;		/* FM/MFM marks bits */
  libspectrum_byte *weak;	/* weak marks bits/weak data */
  int i;			/* index for track and clocks */
  disk_type_t type;		/* DISK_UDI, ... */
  disk_dens_t density;		/* DISK_SD DISK_DD, or DISK_HD */
} disk_t;

/* every track data:
TRACK_LEN TYPE TRACK......DATA CLOCK..MARKS MF..MARKS WEAK..MARKS
               ^               ^            ^         ^
               |__ track       |__ clocks   |__ mf    |__ weak
  so, track[-1] = TYPE
  TLEN = track[-3] + 256 * track[-2]
  TYPE is Track type as in UDI spec (0x00, 0x01, 0x02, 0x80, 0x81, 0x82) after update_tracks_mode() !!!
*/

#define DISK_CLEN( bpt ) ( ( bpt ) / 8 + ( ( bpt ) % 8 ? 1 : 0 ) )

#define DISK_SET_TRACK_IDX( d, idx ) \
   d->track = d->data + 3 + ( idx ) * d->tlen; \
   d->c_bpt = d->track[-3] + 256 * d->track[-2]; \
   d->clocks = d->track  + d->c_bpt; \
   d->fm     = d->clocks + DISK_CLEN( d->c_bpt ); \
   d->weak   = d->fm     + DISK_CLEN( d->c_bpt )

#define DISK_SET_TRACK( d, head, cyl ) \
   DISK_SET_TRACK_IDX( (d), (d)->sides * cyl + head )

typedef struct disk_position_context_t {
  libspectrum_byte *track;   /* current track data bytes */
  libspectrum_byte *clocks;  /* clock marks bits */
  libspectrum_byte *fm;      /* FM/MFM marks bits */
  libspectrum_byte *weak;    /* weak marks bits/weak data */
  int i;                     /* index for track and clocks */
  int c_bpt;
} disk_position_context_t;

const char *disk_strerror( int error );
/* create an unformatted disk sides -> (1/2) cylinders -> track/side,
   dens -> 'density' related to unformatted length of a track (SD = 3125,
   DD = 6250, HD = 12500, type -> if write this disk we want to convert
   into this type of image file
*/
int disk_new( disk_t *d, int sides, int cylinders, disk_dens_t dens, disk_type_t type );
/* open a disk image file. if preindex = 1 and the image file not UDI then
   pre-index gap generated with index mark (0xfc)
   this time only .mgt(.dsk)/.img/.udi and CPC/extended CPC file format
   supported
*/
int disk_open( disk_t *d, const char *filename, int preindex, int disk_merge );
/* merge two one sided disk (d1, d2) to a two sided one (d),
   after merge closes d1 and d2
*/
int disk_merge_sides( disk_t *d, disk_t *d1, disk_t *d2, int autofill );
/* write a disk image file (from the disk buffer). the d->type
   gives the format of file. if it DISK_TYPE_AUTO, disk_write
   try to guess from the file name (extension). if fail save as
   UDI.
*/
int disk_write( disk_t *d, const char *filename );
/* format disk to plus3 accept for formatting
*/
int disk_preformat( disk_t *d );
/* close a disk and free buffers
*/
void disk_close( disk_t *d );

#endif /* FUSE_DISK_H */
