/*
 * CiderPress
 * Copyright (C) 2007 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * Support for the "2MG"/"2IMG" disk image format.
 *
 * This gets its own header because CiderPress uses these definitions and
 * functions directly.
 */
#ifndef DISKIMG_TWOIMG_H
#define DISKIMG_TWOIMG_H

#include "DiskImg.h"

namespace DiskImgLib {

/*
 * 2IMG header definition (was on http://www.magnet.ch/emutech/Tech/,
 * now on http://www.a2central.com/programming/filetypes/ftne00130.html
 * as filetype $e0/$0130).
 *
 * Meaning of "flags":
 *  bit 31 : disk is "locked"; used by emulators as write-protect sticker.
 *  bit  8 : if set, bits 0-7 specify DOS 3.3 volume number
 *  bit 0-7: if bit 8 is set, use this as DOS volume; else use 254
 *
 * All values are stored little-endian.
 */
class DISKIMG_API TwoImgHeader {
public:
    TwoImgHeader(void) :
        fMagic(0),
        fCreator(0),
        fHeaderLen(0),
        fVersion(0),
        fImageFormat(0),
        fFlags(0),
        fNumBlocks(0),
        fDataOffset(0),
        fDataLen(0),
        fCmtOffset(0),
        fCmtLen(0),
        fCreatorOffset(0),
        fCreatorLen(0),
        fSpare(),

        fDOSVolumeNum(-1),
        fMagicStr(),
        fCreatorStr(),
        fComment(NULL),
        fCreatorChunk(NULL)
    {}
    virtual ~TwoImgHeader(void) {
        delete[] fComment;
        delete[] fCreatorChunk;
    }

    /*
     * Header fields.
     */
    //char            fMagic[4];
    //char            fCreator[4];
    uint32_t    fMagic;
    uint32_t    fCreator;
    uint16_t    fHeaderLen;
    uint16_t    fVersion;
    uint32_t    fImageFormat;
    uint32_t    fFlags;         // may include DOS volume num
    uint32_t    fNumBlocks;     // 512-byte blocks
    uint32_t    fDataOffset;
    uint32_t    fDataLen;
    uint32_t    fCmtOffset;
    uint32_t    fCmtLen;
    uint32_t    fCreatorOffset;
    uint32_t    fCreatorLen;
    uint32_t    fSpare[4];

    /*
     * Related constants.
     */
    enum {
        // imageFormat
        kImageFormatDOS     = 0,
        kImageFormatProDOS  = 1,
        kImageFormatNibble  = 2,
        // flags
        kFlagLocked         = (1L<<31),
        kDOSVolumeSet       = (1L<<8),
        kDOSVolumeMask      = (0xff),
        kDefaultVolumeNum   = 254,

        // constants used when creating a new header
        kOurHeaderLen       = 64,
        kOurVersion         = 1,

        kBlockSize          = 512,
        kMagic              = 0x32494d47,       // 2IMG
        kCreatorCiderPress  = 0x43647250,       // CdrP
        kCreatorSweet16     = 0x574f4f46,       // WOOF
    };

    /*
     * Basic functions.
     *
     * The read header function will read the comment, but the write
     * header function will not.  This is because the GenericFD functions
     * don't allow seeking past the current EOF.
     *
     * ReadHeader/WriteHeader expect the file to be seeked to the initial
     * offset.  WriteFooter expects the file to be seeked just past the
     * end of the data section.  This is done in case the file has some
     * sort of wrapper outside the 2MG header.
     */
    int InitHeader(int imageFormat, uint32_t imageSize, uint32_t imageBlockCount);
    int ReadHeader(FILE* fp, uint32_t totalLength);
    int ReadHeader(GenericFD* pGFD, uint32_t totalLength);
    int WriteHeader(FILE* fp) const;
    int WriteHeader(GenericFD* pGFD) const;
    int WriteFooter(FILE* fp) const;
    int WriteFooter(GenericFD* pGFD) const;
    void DumpHeader(void) const;        // for debugging

    /*
     * Getters & setters.
     */
    const char* GetMagicStr(void) const { return fMagicStr; }
    const char* GetCreatorStr(void) const { return fCreatorStr; }

    int16_t GetDOSVolumeNum(void) const;
    void SetDOSVolumeNum(short dosVolumeNum);
    const char* GetComment(void) const { return fComment; }
    void SetComment(const char* comment);
    const void* GetCreatorChunk(void) const { return fCreatorChunk; }
    void SetCreatorChunk(const void* creatorBlock, long len);

private:
    int UnpackHeader(const uint8_t* buf, uint32_t totalLength);
    void PackHeader(uint8_t* buf) const;
    int GetChunk(GenericFD* pGFD, di_off_t relOffset, long len, void** pBuf);
    int GetChunk(FILE* fp, di_off_t relOffset, long len, void** pBuf);

    int16_t fDOSVolumeNum;      // 8-bit volume number, or -1
    char    fMagicStr[5];
    char    fCreatorStr[5];

    char*   fComment;
    char*   fCreatorChunk;
};

}   // namespace DiskImgLib

#endif /*DISKIMG_TWOIMG_H*/
