/*
 * libhfs - library for reading and writing Macintosh HFS volumes
 * Copyright (C) 1996-1998 Robert Leslie
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: libhfs.h,v 1.7 1998/11/02 22:09:02 rob Exp $
 */

#include "hfs.h"
#include "apple.h"

#define ERROR(code, str)                   \
    do                                     \
    {                                      \
        hfs_error = (str), errno = (code); \
        goto fail;                         \
    } while (0)

#ifdef DEBUG
#define ASSERT(cond) \
    do               \
    {                \
        if (!(cond)) \
            abort(); \
    } while (0)
#else
#define ASSERT(cond) /* nothing */
#endif

#define SIZE(type, n) ((size_t)(sizeof(type) * (n)))
#define ALLOC(type, n) ((type*)malloc(SIZE(type, n)))
#define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type*)0)
#define FREE(ptr) ((ptr) ? (void)free((void*)ptr) : (void)0)

#define REALLOC(ptr, type, n) \
    ((type*)((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n))))
#define REALLOCX(ptr, type, n) \
    ((n) ? REALLOC(ptr, type, n) : (FREE(ptr), (type*)0))

#define BMTST(bm, num) \
    (((const byte*)(bm))[(num) >> 3] & (0x80 >> ((num)&0x07)))
#define BMSET(bm, num) (((byte*)(bm))[(num) >> 3] |= (0x80 >> ((num)&0x07)))
#define BMCLR(bm, num) (((byte*)(bm))[(num) >> 3] &= ~(0x80 >> ((num)&0x07)))

#define STRINGIZE(x) #x
#define STR(x) STRINGIZE(x)

typedef unsigned char byte;
typedef byte block[HFS_BLOCKSZ];

typedef struct _bucket_
{
    int flags;          /* bit flags */
    unsigned int count; /* number of times this block is requested */

    unsigned long bnum; /* logical block number */
    block* data;        /* pointer to block contents */

    struct _bucket_* cnext; /* next bucket in cache chain */
    struct _bucket_* cprev; /* previous bucket in cache chain */

    struct _bucket_* hnext;  /* next bucket in hash chain */
    struct _bucket_** hprev; /* previous bucket's pointer to this bucket */
} bucket;

#define HFS_BUCKET_INUSE 0x01
#define HFS_BUCKET_DIRTY 0x02

#define HFS_CACHESZ 128
#define HFS_HASHSZ 32
#define HFS_BLOCKBUFSZ 16

typedef struct
{
    struct _hfsvol_* vol; /* volume to which cache belongs */
    bucket* tail;         /* end of bucket chain */

    unsigned int hits;   /* number of cache hits */
    unsigned int misses; /* number of cache misses */

    bucket chain[HFS_CACHESZ]; /* cache bucket chain */
    bucket* hash[HFS_HASHSZ];  /* hash table for bucket chain */

    block pool[HFS_CACHESZ]; /* physical blocks in cache */
} bcache;

#define HFS_MAP1SZ 256
#define HFS_MAPXSZ 492

#define HFS_NODEREC(nd, rnum) ((nd).data + (nd).roff[rnum])
#define HFS_RECLEN(nd, rnum) ((nd).roff[(rnum) + 1] - (nd).roff[rnum])

#define HFS_RECKEYLEN(ptr) (*(const byte*)(ptr))
#define HFS_RECKEYSKIP(ptr) ((size_t)((1 + HFS_RECKEYLEN(ptr) + 1) & ~1))
#define HFS_RECDATA(ptr) ((ptr) + HFS_RECKEYSKIP(ptr))

#define HFS_SETKEYLEN(ptr, x) (*(byte*)(ptr) = (x))

#define HFS_CATDATALEN sizeof(CatDataRec)
#define HFS_EXTDATALEN sizeof(ExtDataRec)
#define HFS_MAX_DATALEN \
    (HFS_CATDATALEN > HFS_EXTDATALEN ? HFS_CATDATALEN : HFS_EXTDATALEN)

#define HFS_CATKEYLEN sizeof(CatKeyRec)
#define HFS_EXTKEYLEN sizeof(ExtKeyRec)
#define HFS_MAX_KEYLEN \
    (HFS_CATKEYLEN > HFS_EXTKEYLEN ? HFS_CATKEYLEN : HFS_EXTKEYLEN)

#define HFS_MAX_CATRECLEN (HFS_CATKEYLEN + HFS_CATDATALEN)
#define HFS_MAX_EXTRECLEN (HFS_EXTKEYLEN + HFS_EXTDATALEN)
#define HFS_MAX_RECLEN (HFS_MAX_KEYLEN + HFS_MAX_DATALEN)

#define HFS_SIGWORD 0x4244
#define HFS_SIGWORD_MFS ((Integer)0xd2d7)

#define HFS_ATRB_BUSY (1 << 6)
#define HFS_ATRB_HLOCKED (1 << 7)
#define HFS_ATRB_UMOUNTED (1 << 8)
#define HFS_ATRB_BBSPARED (1 << 9)
#define HFS_ATRB_BVINCONSIS (1 << 11)
#define HFS_ATRB_COPYPROT (1 << 14)
#define HFS_ATRB_SLOCKED (1 << 15)

struct _hfsfile_
{
    struct _hfsvol_* vol;        /* pointer to volume descriptor */
    unsigned long parid;         /* parent directory ID of this file */
    char name[HFS_MAX_FLEN + 1]; /* catalog name of this file */
    CatDataRec cat;              /* catalog information */
    ExtDataRec ext;              /* current extent record */
    unsigned int fabn;           /* starting file allocation block number */
    int fork;                    /* current selected fork for I/O */
    unsigned long pos;           /* current file seek pointer */
    int flags;                   /* bit flags */

    struct _hfsfile_* prev;
    struct _hfsfile_* next;
};

#define HFS_FILE_UPDATE_CATREC 0x01

#define HFS_MAX_NRECS 35 /* maximum based on minimum record size */

typedef struct _node_
{
    struct _btree_* bt; /* btree to which this node belongs */
    unsigned long nnum; /* node index */
    NodeDescriptor nd;  /* node descriptor */
    int rnum;           /* current record index */
    UInteger roff[HFS_MAX_NRECS + 1];
    /* record offsets */
    block data; /* raw contents of node */
} node;

struct _hfsdir_
{
    struct _hfsvol_* vol; /* associated volume */
    unsigned long dirid;  /* directory ID of interest (or 0) */

    node n;                /* current B*-tree node */
    struct _hfsvol_* vptr; /* current volume pointer */

    struct _hfsdir_* prev;
    struct _hfsdir_* next;
};

typedef void (*keyunpackfunc)(const byte*, void*);
typedef int (*keycomparefunc)(const void*, const void*);

typedef struct _btree_
{
    hfsfile f;           /* subset file information */
    node hdrnd;          /* header node */
    BTHdrRec hdr;        /* header record */
    byte* map;           /* usage bitmap */
    unsigned long mapsz; /* number of bytes in bitmap */
    int flags;           /* bit flags */

    keyunpackfunc keyunpack;   /* key unpacking function */
    keycomparefunc keycompare; /* key comparison function */
} btree;

#define HFS_BT_UPDATE_HDR 0x01

struct _hfsvol_
{
    void* priv; /* OS-dependent private descriptor data */
    int flags;  /* bit flags */

    int pnum;             /* ordinal HFS partition number */
    unsigned long vstart; /* logical block offset to start of volume */
    unsigned long vlen;   /* number of logical blocks in volume */
    unsigned int lpa;     /* number of logical blocks per allocation block */

    bcache* cache; /* cache of recently used blocks */

    MDB mdb;              /* master directory block */
    block* vbm;           /* volume bitmap */
    unsigned short vbmsz; /* number of blocks in bitmap */

    btree ext; /* B*-tree control block for extents overflow file */
    btree cat; /* B*-tree control block for catalog file */

    unsigned long cwd; /* directory id of current working directory */

    int refs;       /* number of external references to this volume */
    hfsfile* files; /* list of open files */
    hfsdir* dirs;   /* list of open directories */

    struct _hfsvol_* prev;
    struct _hfsvol_* next;
};

#define HFS_VOL_OPEN 0x0001
#define HFS_VOL_MOUNTED 0x0002
#define HFS_VOL_READONLY 0x0004
#define HFS_VOL_USINGCACHE 0x0008

#define HFS_VOL_UPDATE_MDB 0x0010
#define HFS_VOL_UPDATE_ALTMDB 0x0020
#define HFS_VOL_UPDATE_VBM 0x0040

#define HFS_VOL_OPT_MASK 0xff00

extern hfsvol* hfs_mounts;
