func (mc MappedCluster) IsBad() bool {
	return mc == 0xfffffff7
}
IsBad indicates that this cluster has been marked as having one or more bad
sectors (which is somewhat a waste of space).
package exfat
import (
	"bytes"
	"fmt"
	"io"
	"math"
	"os"
	"reflect"
	"encoding/binary"
	"github.com/dsoprea/go-logging"
	"github.com/go-restruct/restruct"
)
const (
	bootSectorHeaderSize        = 512
	oemParametersSize           = 48 * 10
	mainExtendedBootSectorCount = 8
)
var (
	requiredJumpBootSignature     = []byte{0xeb, 0x76, 0x90}
	requiredFileSystemName        = []byte("EXFAT   ")
	requiredBootSignature         = uint16(0xaa55)
	requiredExtendedBootSignature = uint32(0xaa550000)
)
type bootRegion struct {
	bsh        BootSectorHeader
	sectorSize uint32
}
type ExfatReader struct {
	rs io.ReadSeeker
	bootRegion bootRegion
	activeFat Fat
}
func NewExfatReader(rs io.ReadSeeker) *ExfatReader {
	return &ExfatReader{
		rs: rs,
	}
}
func (er *ExfatReader) parseN(byteCount int, x interface{}) (err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	raw := make([]byte, byteCount)
	_, err = io.ReadFull(er.rs, raw)
	log.PanicIf(err)
	err = restruct.Unpack(raw, defaultEncoding, x)
	log.PanicIf(err)
	return nil
}
type BootSectorHeader struct {
	JumpBoot [3]byte
	FileSystemName [8]byte
	MustBeZero [53]byte
	PartitionOffset uint64
	VolumeLength uint64
	FatOffset uint32
	FatLength uint32
	ClusterHeapOffset uint32
	ClusterCount uint32
	FirstClusterOfRootDirectory uint32
	VolumeSerialNumber uint32
	FileSystemRevision [2]uint8
	VolumeFlags VolumeFlags
	BytesPerSectorShift uint8
	SectorsPerClusterShift uint8
	NumberOfFats uint8
	DriveSelect uint8
	PercentInUse uint8
	Reserved [7]byte
	BootCode [390]byte
	BootSignature uint16
}
const (
	VolumeFlagActiveFat VolumeFlags = 1
	VolumeFlagVolumeDirty = 2
	VolumeFlagMediaFailure = 4
	VolumeFlagClearToZero = 8
)
type VolumeFlags uint16
func (vf VolumeFlags) UseFirstFat() bool {
	return vf&VolumeFlagActiveFat == 0
}
func (vf VolumeFlags) UseSecondFat() bool {
	return vf&VolumeFlagActiveFat > 0
}
func (vf VolumeFlags) IsDirty() bool {
	return vf&VolumeFlagVolumeDirty > 0
}
func (vf VolumeFlags) HasHadMediaFailures() bool {
	return vf&VolumeFlagMediaFailure > 0
}
func (vf VolumeFlags) ClearToZero() bool {
	return vf&VolumeFlagClearToZero > 0
}
func (vf VolumeFlags) DumpBareIndented(indent string) {
	fmt.Printf("%sRaw Value: (%08b)\n", indent, vf)
	fmt.Printf("%sUseFirstFat: [%v]\n", indent, vf.UseFirstFat())
	fmt.Printf("%sUseSecondFat: [%v]\n", indent, vf.UseSecondFat())
	fmt.Printf("%sIsDirty: [%v]\n", indent, vf.IsDirty())
	fmt.Printf("%sHasHadMediaFailures: [%v]\n", indent, vf.HasHadMediaFailures())
	fmt.Printf("%sClearToZero: [%v]\n", indent, vf.ClearToZero())
}
func (bsh BootSectorHeader) SectorSize() uint32 {
	return uint32(math.Pow(2, float64(bsh.BytesPerSectorShift)))
}
func (bsh BootSectorHeader) SectorsPerCluster() uint32 {
	return uint32(math.Pow(float64(2), float64(bsh.SectorsPerClusterShift)))
}
func (bsh BootSectorHeader) Dump() {
	fmt.Printf("Boot Sector Header\n")
	fmt.Printf("==================\n")
	fmt.Printf("\n")
	fmt.Printf("PartitionOffset: (%d)\n", bsh.PartitionOffset)
	fmt.Printf("VolumeLength: (%d)\n", bsh.VolumeLength)
	fmt.Printf("FatOffset: (%d)\n", bsh.FatOffset)
	fmt.Printf("FatLength: (%d)\n", bsh.FatLength)
	fmt.Printf("ClusterHeapOffset: (%d)\n", bsh.ClusterHeapOffset)
	fmt.Printf("ClusterCount: (%d)\n", bsh.ClusterCount)
	fmt.Printf("FirstClusterOfRootDirectory: (%d)\n", bsh.FirstClusterOfRootDirectory)
	fmt.Printf("VolumeSerialNumber: (0x%08x)\n", bsh.VolumeSerialNumber)
	fmt.Printf("FileSystemRevision: (0x%02x) (0x%02x)\n", bsh.FileSystemRevision[0], bsh.FileSystemRevision[1])
	fmt.Printf("BytesPerSectorShift: (%d)\n", bsh.BytesPerSectorShift)
	fmt.Printf("-> Sector-size: 2^(%d) -> %d\n", bsh.BytesPerSectorShift, bsh.SectorSize())
	fmt.Printf("SectorsPerClusterShift: (%d)\n", bsh.SectorsPerClusterShift)
	fmt.Printf("-> Sectors-per-cluster: 2^(%d) -> %d\n", bsh.SectorsPerClusterShift, bsh.SectorsPerCluster())
	fmt.Printf("NumberOfFats: (%d)\n", bsh.NumberOfFats)
	fmt.Printf("DriveSelect: (%d)\n", bsh.DriveSelect)
	fmt.Printf("PercentInUse: (%d)\n", bsh.PercentInUse)
	fmt.Printf("\n")
	fmt.Printf("VolumeFlags: (%d)\n", bsh.VolumeFlags)
	bsh.VolumeFlags.DumpBareIndented("  ")
	fmt.Printf("\n")
}
func (bsh BootSectorHeader) String() string {
	return fmt.Sprintf("BootSector<SN=(0x%08x) REVISION=(0x%02x)-(0x%02x)>", bsh.VolumeSerialNumber, bsh.FileSystemRevision[0], bsh.FileSystemRevision[1])
}
func (er *ExfatReader) readBootSectorHead() (bsh BootSectorHeader, sectorSize uint32, err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	err = er.parseN(bootSectorHeaderSize, &bsh)
	log.PanicIf(err)
	if bytes.Equal(bsh.JumpBoot[:], requiredJumpBootSignature) != true {
		log.Panicf("jump-boot value not correct: %x", bsh.JumpBoot[:])
	} else if bytes.Equal(bsh.FileSystemName[:], requiredFileSystemName) != true {
		log.Panicf("filesystem name not correct: %x [%s]", bsh.FileSystemName, string(bsh.FileSystemName[:]))
	} else if bsh.BootSignature != requiredBootSignature {
		log.Panicf("boot-signature not correct: %x", bsh.BootSignature)
	}
	for _, c := range bsh.MustBeZero {
		if c != 0 {
			log.Panicf("must-be-zero field not all zeros")
		}
	}
	sectorSize = bsh.SectorSize()
	excessByteCount := sectorSize - 512
	if excessByteCount != 0 {
		_, err := er.rs.Seek(int64(excessByteCount), os.SEEK_CUR)
		log.PanicIf(err)
	}
	return bsh, sectorSize, nil
}
type ExtendedBootCode []byte
func (er *ExfatReader) readExtendedBootSector(sectorSize uint32) (extendedBootCode ExtendedBootCode, err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	extendedBootCodeSize := sectorSize - 4
	extendedBootCode = make(ExtendedBootCode, extendedBootCodeSize)
	_, err = io.ReadFull(er.rs, extendedBootCode)
	log.PanicIf(err)
	extendedBootSignature := uint32(0)
	err = binary.Read(er.rs, defaultEncoding, &extendedBootSignature)
	log.PanicIf(err)
	if extendedBootSignature != requiredExtendedBootSignature {
		log.Panicf("extended boot-signature not correct: %x", extendedBootSignature)
	}
	return extendedBootCode, nil
}
func (er *ExfatReader) readExtendedBootSectors(sectorSize uint32) (extendedBootCodeList [mainExtendedBootSectorCount]ExtendedBootCode, err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	for i := 0; i < mainExtendedBootSectorCount; i++ {
		extendedBootCode, err := er.readExtendedBootSector(sectorSize)
		log.PanicIf(err)
		extendedBootCodeList[i] = extendedBootCode
	}
	return extendedBootCodeList, nil
}
type OemParameter struct {
	Parameter [48]byte
}
type OemParameters struct {
	Parameters [10]OemParameter
}
func (er *ExfatReader) readOemParameters(sectorSize uint32) (oemParameters OemParameters, err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	err = er.parseN(oemParametersSize, &oemParameters)
	log.PanicIf(err)
	remainder := sectorSize - 480
	buffer := make([]byte, remainder)
	_, err = io.ReadFull(er.rs, buffer)
	log.PanicIf(err)
	return oemParameters, nil
}
func (er *ExfatReader) readMainReserved(sectorSize uint32) (err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	buffer := make([]byte, sectorSize)
	_, err = io.ReadFull(er.rs, buffer)
	log.PanicIf(err)
	return nil
}
func (er *ExfatReader) readMainBootChecksum(sectorSize uint32) (err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	buffer := make([]byte, sectorSize)
	_, err = io.ReadFull(er.rs, buffer)
	log.PanicIf(err)
	return nil
}
func (er *ExfatReader) getCurrentSector() (sector uint32, offset uint32) {
	sectorSize := er.SectorSize()
	currentOffsetRaw, err := er.rs.Seek(0, os.SEEK_CUR)
	log.PanicIf(err)
	currentOffset := uint32(currentOffsetRaw)
	return currentOffset / sectorSize, currentOffset % sectorSize
}
func (er *ExfatReader) printCurrentSector() {
	sectorSize := er.SectorSize()
	currentOffsetRaw, err := er.rs.Seek(0, os.SEEK_CUR)
	log.PanicIf(err)
	currentOffset := uint32(currentOffsetRaw)
	fmt.Printf("CURRENT SECTOR: (%d) (%d)\n", currentOffset/sectorSize, currentOffset%sectorSize)
}
func (er *ExfatReader) assertAlignedToSector() {
	sectorSize := er.SectorSize()
	currentOffsetRaw, err := er.rs.Seek(0, os.SEEK_CUR)
	log.PanicIf(err)
	currentOffset := uint32(currentOffsetRaw)
	if currentOffset%sectorSize != 0 {
		log.Panicf("not currently aligned to a sector: (%d) (%d)", currentOffset/sectorSize, currentOffset%sectorSize)
	}
}
func (er *ExfatReader) parseBootRegion() (br bootRegion, err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	bsh, sectorSize, err := er.readBootSectorHead()
	log.PanicIf(err)
	_, err = er.readExtendedBootSectors(sectorSize)
	log.PanicIf(err)
	_, err = er.readOemParameters(sectorSize)
	log.PanicIf(err)
	err = er.readMainReserved(sectorSize)
	log.PanicIf(err)
	err = er.readMainBootChecksum(sectorSize)
	log.PanicIf(err)
	br = bootRegion{
		bsh: bsh,
	}
	return br, nil
}
func (er *ExfatReader) selectBootRegion(bootRegionMain, bootRegionBackup bootRegion) (err error) {
	defer func() {
		if errRaw := recover(); errRaw != nil {
			err = log.Wrap(errRaw.(error))
		}
	}()
	er.bootRegion = bootRegionMain
	return nil
}
type MappedCluster uint32