/***************************************************************************

    MAME Compressed Hunks of Data file format

    Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

***************************************************************************/

#pragma once

#ifndef __CHD_H__
#define __CHD_H__

#include "osdcore.h"


/***************************************************************************

    Compressed Hunks of Data header format. All numbers are stored in
    Motorola (big-endian) byte ordering. The header is 76 (V1) or 80 (V2)
    bytes long.

    V1 header:

    [  0] char   tag[8];        // 'MComprHD'
    [  8] UINT32 length;        // length of header (including tag and length fields)
    [ 12] UINT32 version;       // drive format version
    [ 16] UINT32 flags;         // flags (see below)
    [ 20] UINT32 compression;   // compression type
    [ 24] UINT32 hunksize;      // 512-byte sectors per hunk
    [ 28] UINT32 totalhunks;    // total # of hunks represented
    [ 32] UINT32 cylinders;     // number of cylinders on hard disk
    [ 36] UINT32 heads;         // number of heads on hard disk
    [ 40] UINT32 sectors;       // number of sectors on hard disk
    [ 44] UINT8  md5[16];       // MD5 checksum of raw data
    [ 60] UINT8  parentmd5[16]; // MD5 checksum of parent file
    [ 76] (V1 header length)

    V2 header:

    [  0] char   tag[8];        // 'MComprHD'
    [  8] UINT32 length;        // length of header (including tag and length fields)
    [ 12] UINT32 version;       // drive format version
    [ 16] UINT32 flags;         // flags (see below)
    [ 20] UINT32 compression;   // compression type
    [ 24] UINT32 hunksize;      // seclen-byte sectors per hunk
    [ 28] UINT32 totalhunks;    // total # of hunks represented
    [ 32] UINT32 cylinders;     // number of cylinders on hard disk
    [ 36] UINT32 heads;         // number of heads on hard disk
    [ 40] UINT32 sectors;       // number of sectors on hard disk
    [ 44] UINT8  md5[16];       // MD5 checksum of raw data
    [ 60] UINT8  parentmd5[16]; // MD5 checksum of parent file
    [ 76] UINT32 seclen;        // number of bytes per sector
    [ 80] (V2 header length)

    V3 header:

    [  0] char   tag[8];        // 'MComprHD'
    [  8] UINT32 length;        // length of header (including tag and length fields)
    [ 12] UINT32 version;       // drive format version
    [ 16] UINT32 flags;         // flags (see below)
    [ 20] UINT32 compression;   // compression type
    [ 24] UINT32 totalhunks;    // total # of hunks represented
    [ 28] UINT64 logicalbytes;  // logical size of the data (in bytes)
    [ 36] UINT64 metaoffset;    // offset to the first blob of metadata
    [ 44] UINT8  md5[16];       // MD5 checksum of raw data
    [ 60] UINT8  parentmd5[16]; // MD5 checksum of parent file
    [ 76] UINT32 hunkbytes;     // number of bytes per hunk
    [ 80] UINT8  sha1[20];      // SHA1 checksum of raw data
    [100] UINT8  parentsha1[20];// SHA1 checksum of parent file
    [120] (V3 header length)

    Flags:
        0x00000001 - set if this drive has a parent
        0x00000002 - set if this drive allows writes

***************************************************************************/


/***************************************************************************
    CONSTANTS
***************************************************************************/

/* header information */
#define CHD_HEADER_VERSION			3
#define CHD_V1_HEADER_SIZE			76
#define CHD_V2_HEADER_SIZE			80
#define CHD_V3_HEADER_SIZE			120
#define CHD_MAX_HEADER_SIZE			CHD_V3_HEADER_SIZE

/* checksumming information */
#define CHD_MD5_BYTES				16
#define CHD_SHA1_BYTES				20

/* CHD global flags */
#define CHDFLAGS_HAS_PARENT			0x00000001
#define CHDFLAGS_IS_WRITEABLE		0x00000002
#define CHDFLAGS_UNDEFINED			0xfffffffc

/* compression types */
#define CHDCOMPRESSION_NONE			0
#define CHDCOMPRESSION_ZLIB			1
#define CHDCOMPRESSION_ZLIB_PLUS	2
#define CHDCOMPRESSION_AV			3

/* A/V codec configuration parameters */
#define AV_CODEC_COMPRESS_CONFIG	0
#define AV_CODEC_DECOMPRESS_CONFIG	1

/* metadata parameters */
#define CHDMETATAG_WILDCARD			0
#define CHD_METAINDEX_APPEND		((UINT32)-1)

/* standard hard disk metadata */
#define HARD_DISK_METADATA_TAG		0x47444444	/* 'GDDD' */
#define HARD_DISK_METADATA_FORMAT	"CYLS:%d,HEADS:%d,SECS:%d,BPS:%d"

/* standard CD-ROM metadata */
#define CDROM_OLD_METADATA_TAG		0x43484344	/* 'CHCD' */
#define CDROM_TRACK_METADATA_TAG	0x43485452	/* 'CHTR' */
#define CDROM_TRACK_METADATA_FORMAT	"TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d"

/* standard A/V metadata */
#define AV_METADATA_TAG				0x41564156	/* 'AVAV' */
#define AV_METADATA_FORMAT			"FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d META:%d"

/* CHD open values */
#define CHD_OPEN_READ				1
#define CHD_OPEN_READWRITE			2

/* error types */
enum _chd_error
{
	CHDERR_NONE,
	CHDERR_NO_INTERFACE,
	CHDERR_OUT_OF_MEMORY,
	CHDERR_INVALID_FILE,
	CHDERR_INVALID_PARAMETER,
	CHDERR_INVALID_DATA,
	CHDERR_FILE_NOT_FOUND,
	CHDERR_REQUIRES_PARENT,
	CHDERR_FILE_NOT_WRITEABLE,
	CHDERR_READ_ERROR,
	CHDERR_WRITE_ERROR,
	CHDERR_CODEC_ERROR,
	CHDERR_INVALID_PARENT,
	CHDERR_HUNK_OUT_OF_RANGE,
	CHDERR_DECOMPRESSION_ERROR,
	CHDERR_COMPRESSION_ERROR,
	CHDERR_CANT_CREATE_FILE,
	CHDERR_CANT_VERIFY,
	CHDERR_NOT_SUPPORTED,
	CHDERR_METADATA_NOT_FOUND,
	CHDERR_INVALID_METADATA_SIZE,
	CHDERR_UNSUPPORTED_VERSION,
	CHDERR_VERIFY_INCOMPLETE,
	CHDERR_INVALID_METADATA,
	CHDERR_INVALID_STATE,
	CHDERR_OPERATION_PENDING,
	CHDERR_NO_ASYNC_OPERATION,
	CHDERR_UNSUPPORTED_FORMAT
};
typedef enum _chd_error chd_error;



/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

/* opaque types */
typedef struct _chd_file chd_file;
typedef struct _chd_interface_file chd_interface_file;


/* progress callback function */
typedef void (*chd_progress_printf)(const char *format, ...);


/* extract header structure (NOT the on-disk header structure) */
typedef struct _chd_header chd_header;
struct _chd_header
{
	UINT32	length;						/* length of header data */
	UINT32	version;					/* drive format version */
	UINT32	flags;						/* flags field */
	UINT32	compression;				/* compression type */
	UINT32	hunkbytes;					/* number of bytes per hunk */
	UINT32	totalhunks;					/* total # of hunks represented */
	UINT64	logicalbytes;				/* logical size of the data */
	UINT64	metaoffset;					/* offset in file of first metadata */
	UINT8	md5[CHD_MD5_BYTES];			/* MD5 checksum of raw data */
	UINT8	parentmd5[CHD_MD5_BYTES];	/* MD5 checksum of parent file */
	UINT8	sha1[CHD_SHA1_BYTES];		/* SHA1 checksum of raw data */
	UINT8	parentsha1[CHD_SHA1_BYTES];	/* SHA1 checksum of parent file */

	UINT32	obsolete_cylinders;			/* obsolete field -- do not use! */
	UINT32	obsolete_sectors;			/* obsolete field -- do not use! */
	UINT32	obsolete_heads;				/* obsolete field -- do not use! */
	UINT32	obsolete_hunksize;			/* obsolete field -- do not use! */
};


/* file I/O interface */
typedef struct _chd_interface chd_interface;
struct _chd_interface
{
	chd_interface_file *(*open)(const char *filename, const char *mode);
	void (*close)(chd_interface_file *file);
	UINT32 (*read)(chd_interface_file *file, UINT64 offset, UINT32 count, void *buffer);
	UINT32 (*write)(chd_interface_file *file, UINT64 offset, UINT32 count, const void *buffer);
	UINT64 (*length)(chd_interface_file *file);
};


/* A/V codec decompression configuration */
typedef struct _av_codec_decompress_config av_codec_decompress_config;
struct _av_codec_decompress_config
{
	UINT32	decode_mask;				/* decoding mask */
	UINT8 *	video_buffer;				/* pointer to alternate video buffer */
	UINT32	video_stride;				/* bytes between rows in video buffer */
	UINT32	video_xor;					/* XOR to apply to bytes in video buffer */
	UINT32	audio_xor;					/* XOR to apply to bytes in audio buffer */
};



/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/


/* ----- external interfacing ----- */

/* set the interface callbacks used for file I/O */
void chd_set_interface(chd_interface *new_interface);



/* ----- CHD file management ----- */

/* create a new CHD file fitting the given description */
chd_error chd_create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent);

/* open an existing CHD file */
chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd);

/* close a CHD file */
void chd_close(chd_file *chd);

/* close all open CHD files */
void chd_close_all(void);

/* compute the indexed filename for multi-CHD use */
void chd_multi_filename(const char *origname, char *finalname, int index);



/* ----- CHD header management ----- */

/* return a pointer to the extracted CHD header data */
const chd_header *chd_get_header(chd_file *chd);

/* set a modified header */
chd_error chd_set_header(const char *filename, const chd_header *header);



/* ----- core data read/write ----- */

/* read one hunk from the CHD file */
chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer);

/* read one hunk from the CHD file asynchronously */
chd_error chd_read_async(chd_file *chd, UINT32 hunknum, void *buffer);

/* write one hunk to a CHD file */
chd_error chd_write(chd_file *chd, UINT32 hunknum, const void *buffer);

/* write one hunk to a CHD file asynchronously */
chd_error chd_write_async(chd_file *chd, UINT32 hunknum, const void *buffer);

/* wait for a previously issued async read/write to complete and return the error */
chd_error chd_async_complete(chd_file *chd);



/* ----- metadata management ----- */

/* get indexed metadata of a particular sort */
chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag);

/* set indexed metadata of a particular sort */
chd_error chd_set_metadata(chd_file *chd, UINT32 metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen);

/* clone all of the metadata from one CHD to another */
chd_error chd_clone_metadata(chd_file *source, chd_file *dest);



/* ----- compression management ----- */

/* begin compressing data to a CHD */
chd_error chd_compress_begin(chd_file *chd);

/* compress the next hunk of data */
chd_error chd_compress_hunk(chd_file *chd, const void *data, double *curratio);

/* finish compressing data to a CHD */
chd_error chd_compress_finish(chd_file *chd);



/* ----- verification management ----- */

/* begin verifying a CHD */
chd_error chd_verify_begin(chd_file *chd);

/* verify a single hunk of data */
chd_error chd_verify_hunk(chd_file *chd);

/* finish verifying a CHD, returning the computed MD5 and SHA1 */
chd_error chd_verify_finish(chd_file *chd, UINT8 *finalmd5, UINT8 *finalsha1);



/* ----- codec interfaces ----- */

/* set internal codec parameters */
chd_error chd_codec_config(chd_file *chd, int param, void *config);

/* return a string description of a codec */
const char *chd_get_codec_name(UINT32 codec);


#endif /* __CHD_H__ */
