func (gsf GeneralSecondaryFlags) String() string {
	return fmt.Sprintf("GeneralSecondaryFlags<IsAllocationPossible=[%v] NoFatChain=[%v]>",
		gsf.IsAllocationPossible(), gsf.NoFatChain())
}
String returns a descriptive string.
package exfat
import (
	"fmt"
	"reflect"
	"strings"
	"time"
	"github.com/dsoprea/go-logging"
	"github.com/go-restruct/restruct"
)
type EntryType uint8
func (et EntryType) IsEndOfDirectory() bool {
	return et == 0
}
func (et EntryType) IsUnusedEntryMarker() bool {
	return et >= 0x01 && et <= 0x7f
}
func (et EntryType) IsRegular() bool {
	return et >= 0x81 && et <= 0xff
}
func (et EntryType) TypeCode() int {
	return int(et & 31)
}
func (et EntryType) TypeImportance() bool {
	return et&32 > 0
}
func (et EntryType) IsCritical() bool {
	return et.TypeImportance() == false
}
func (et EntryType) IsBenign() bool {
	return et.TypeImportance() == true
}
func (et EntryType) TypeCategory() bool {
	return et&64 > 0
}
func (et EntryType) IsPrimary() bool {
	return et.TypeCategory() == false
}
func (et EntryType) IsSecondary() bool {
	return et.TypeCategory() == true
}
func (et EntryType) IsInUse() bool {
	return et&128 > 0
}
func (et EntryType) Dump() {
	fmt.Printf("Entry Type\n")
	fmt.Printf("==========\n")
	fmt.Printf("\n")
	fmt.Printf("TypeCode: (%d)\n", et.TypeCode())
	fmt.Printf("\n")
	fmt.Printf("TypeImportance: [%v]\n", et.TypeImportance())
	fmt.Printf("- IsCritical: [%v]\n", et.IsCritical())
	fmt.Printf("- IsBenign: [%v]\n", et.IsBenign())
	fmt.Printf("\n")
	fmt.Printf("TypeCategory: [%v]\n", et.TypeCategory())
	fmt.Printf("- IsPrimary: [%v]\n", et.IsPrimary())
	fmt.Printf("- IsSecondary: [%v]\n", et.IsSecondary())
	fmt.Printf("\n")
	fmt.Printf("IsInUse: [%v]\n", et.IsInUse())
	fmt.Printf("\n")
	fmt.Printf("Entry-Type Classes\n")
	fmt.Printf("- IsEndOfDirectory: [%v]\n", et.IsEndOfDirectory())
	fmt.Printf("- IsUnusedEntryMarker: [%v]\n", et.IsUnusedEntryMarker())
	fmt.Printf("- IsRegular: [%v]\n", et.IsRegular())
	fmt.Printf("\n")
}
func (et EntryType) String() string {
	return fmt.Sprintf("EntryType<TYPE-CODE=(%d) IS-CRITICAL=[%v] IS-PRIMARY=[%v] IS-IN-USE=[%v] X-IS-REGULAR=[%v] X-IS-UNUSED=[%v] X-IS-END=[%v]>", et.TypeCode(), et.IsCritical(), et.IsPrimary(), et.IsInUse(), et.IsRegular(), et.IsUnusedEntryMarker(), et.IsEndOfDirectory())
}
type DirectoryEntryParserKey struct {
	typeCode   int
	isCritical bool
	isPrimary  bool
}
func (depk DirectoryEntryParserKey) String() string {
	return fmt.Sprintf("DirectoryEntryParserKey<TYPE-CODE=(%d) IS-CRITICAL=[%v] IS-PRIMARY=[%v]>", depk.typeCode, depk.isCritical, depk.isPrimary)
}
var (
	directoryEntryParsers = map[DirectoryEntryParserKey]reflect.Type{
		{typeCode: 1, isCritical: true, isPrimary: true}: reflect.TypeOf(ExfatAllocationBitmapDirectoryEntry{}),
		{typeCode: 2, isCritical: true, isPrimary: true}: reflect.TypeOf(ExfatUpcaseTableDirectoryEntry{}),
		{typeCode: 3, isCritical: true, isPrimary: true}: reflect.TypeOf(ExfatVolumeLabelDirectoryEntry{}),
		{typeCode: 5, isCritical: true, isPrimary: true}: reflect.TypeOf(ExfatFileDirectoryEntry{}),
		{typeCode: 0, isCritical: false, isPrimary: true}: reflect.TypeOf(ExfatVolumeGuidDirectoryEntry{}),
		{typeCode: 1, isCritical: false, isPrimary: true}: reflect.TypeOf(ExfatTexFATDirectoryEntry{}),
		{typeCode: 0, isCritical: true, isPrimary: false}: reflect.TypeOf(ExfatStreamExtensionDirectoryEntry{}),
		{typeCode: 1, isCritical: true, isPrimary: false}: reflect.TypeOf(ExfatFileNameDirectoryEntry{}),
		{typeCode: 0, isCritical: false, isPrimary: false}: reflect.TypeOf(ExfatVendorExtensionDirectoryEntry{}),
		{typeCode: 1, isCritical: false, isPrimary: false}: reflect.TypeOf(ExfatVendorAllocationDirectoryEntry{}),
	}
)
type DirectoryEntry interface {
	TypeName() string
}
type PrimaryDirectoryEntry interface {
	SecondaryCount() uint8
}
type DumpableDirectoryEntry interface {
	Dump()
}
type ExfatTimestamp uint32
func (et ExfatTimestamp) Second() int {
	return int(et & 31)
}
func (et ExfatTimestamp) Minute() int {
	return int(et&2016) >> 5
}
func (et ExfatTimestamp) Hour() int {
	return int(et&63488) >> 11
}
func (et ExfatTimestamp) Day() int {
	return int(et&2031616) >> 16
}
func (et ExfatTimestamp) Month() int {
	return int(et&31457280) >> 21
}
func (et ExfatTimestamp) Year() int {
	return 1980 + int(et&4261412864)>>25
}
func (et ExfatTimestamp) TimestampWithOffset(offset int) time.Time {
	location := time.FixedZone(fmt.Sprintf("(off=%d)", offset), offset)
	return time.Date(et.Year(), time.Month(et.Month()), et.Day(), et.Hour(), et.Minute(), et.Second(), 0, location)
}
type FileAttributes uint16
func (fa FileAttributes) IsReadOnly() bool {
	return fa&1 > 0
}
func (fa FileAttributes) IsHidden() bool {
	return fa&2 > 0
}
func (fa FileAttributes) IsSystem() bool {
	return fa&4 > 0
}
func (fa FileAttributes) IsDirectory() bool {
	return fa&16 > 0
}
func (fa FileAttributes) IsArchive() bool {
	return fa&32 > 0
}
func (fa FileAttributes) String() string {
	return fmt.Sprintf("FileAttributes<IS-READONLY=[%v] IS-HIDDEN=[%v] IS-SYSTEM=[%v] IS-DIRECTORY=[%v] IS-ARCHIVE=[%v]>",
		fa.IsReadOnly(), fa.IsHidden(), fa.IsSystem(), fa.IsDirectory(), fa.IsArchive())
}
func (fa FileAttributes) DumpBareIndented(indent string) {
	fmt.Printf("%sRead Only? [%v]\n", indent, fa.IsReadOnly())
	fmt.Printf("%sHidden? [%v]\n", indent, fa.IsHidden())
	fmt.Printf("%sSystem? [%v]\n", indent, fa.IsSystem())
	fmt.Printf("%sDirectory? [%v]\n", indent, fa.IsDirectory())
	fmt.Printf("%sArchive? [%v]\n", indent, fa.IsArchive())
}
type ExfatFileDirectoryEntry struct {
	EntryType EntryType
	SecondaryCountRaw uint8
	SetChecksum uint16
	FileAttributes FileAttributes
	Reserved1 uint16
	CreateTimestampRaw ExfatTimestamp
	LastModifiedTimestampRaw ExfatTimestamp
	LastAccessedTimestampRaw ExfatTimestamp
	Create10msIncrement uint8
	LastModified10msIncrement uint8
	CreateUtcOffset uint8
	LastModifiedUtcOffset uint8
	LastAccessedUtcOffset uint8
	Reserved2 [7]byte
}
func (fdf ExfatFileDirectoryEntry) String() string {
	return fmt.Sprintf("FileDirectoryEntry<SECONDARY-COUNT=(%d) CTIME=[%s] MTIME=[%s] ATIME=[%s]>",
		fdf.SecondaryCountRaw,
		fdf.CreateTimestamp(), fdf.LastModifiedTimestamp(), fdf.LastAccessedTimestamp())
}
func (fdf ExfatFileDirectoryEntry) SecondaryCount() uint8 {
	return fdf.SecondaryCountRaw
}
func (fdf ExfatFileDirectoryEntry) TypeName() string {
	return "File"
}
func (fdf ExfatFileDirectoryEntry) CreateTimestamp() time.Time {
	return fdf.CreateTimestampRaw.TimestampWithOffset(int(fdf.CreateUtcOffset))
}
func (fdf ExfatFileDirectoryEntry) LastModifiedTimestamp() time.Time {
	return fdf.LastModifiedTimestampRaw.TimestampWithOffset(int(fdf.LastModifiedUtcOffset))
}
func (fdf ExfatFileDirectoryEntry) LastAccessedTimestamp() time.Time {
	return fdf.LastAccessedTimestampRaw.TimestampWithOffset(int(fdf.LastAccessedUtcOffset))
}
func (fdf ExfatFileDirectoryEntry) Dump() {
	fmt.Printf("File Directory Entry\n")
	fmt.Printf("====================\n")
	fmt.Printf("\n")
	fmt.Printf("SecondaryCount: (%d)\n", fdf.SecondaryCount())
	fmt.Printf("SetChecksum: (0x%04x)\n", fdf.SetChecksum)
	fmt.Printf("CreateTimestamp: [%s]\n", fdf.CreateTimestamp())
	fmt.Printf("LastModifiedTimestamp: [%s]\n", fdf.LastModifiedTimestamp())
	fmt.Printf("LastAccessedTimestamp: [%s]\n", fdf.LastAccessedTimestamp())
	fmt.Printf("\n")
	fmt.Printf("Attributes:\n")
	fdf.FileAttributes.DumpBareIndented("  ")
	fmt.Printf("\n")
}
type ExfatAllocationBitmapDirectoryEntry struct {
	EntryType EntryType
	BitmapFlags uint8
	Reserved [18]byte
	FirstCluster uint32
	DataLength uint64
}
func (abde ExfatAllocationBitmapDirectoryEntry) String() string {
	return fmt.Sprintf("AllocationBitmapDirectoryEntry<BITMAP-FLAGS=[%08b] FIRST-CLUSTER=(%d) DATA-LENGTH=(%d)>", abde.BitmapFlags, abde.FirstCluster, abde.DataLength)
}
func (ExfatAllocationBitmapDirectoryEntry) TypeName() string {
	return "AllocationBitmap"
}
type ExfatUpcaseTableDirectoryEntry struct {
	EntryType EntryType
	Reserved1 [3]byte
	TableChecksum uint32
	Reserved2 [12]byte
	FirstCluster uint32
	DataLength uint64
}
func (utde ExfatUpcaseTableDirectoryEntry) String() string {
	return fmt.Sprintf("UpcaseTableDirectoryEntry<TABLE-CHECKSUM=[0x%08x] FIRST-CLUSTER=(%d) DATA-LENGTH=(%d)>", utde.TableChecksum, utde.FirstCluster, utde.DataLength)
}
func (ExfatUpcaseTableDirectoryEntry) TypeName() string {
	return "UpcaseTable"
}
type ExfatVolumeLabelDirectoryEntry struct {
	EntryType EntryType
	CharacterCount uint8
	VolumeLabel [30]byte
}
func (vlde ExfatVolumeLabelDirectoryEntry) Label() string {
	decodedString := UnicodeFromAscii(vlde.VolumeLabel[:], int(vlde.CharacterCount))
	return decodedString
}
func (vlde ExfatVolumeLabelDirectoryEntry) String() string {
	return fmt.Sprintf("VolumeLabelDirectoryEntry<CHARACTER-COUNT=(%d) LABEL=[%s]>", vlde.CharacterCount, vlde.Label())
}
func (ExfatVolumeLabelDirectoryEntry) TypeName() string {
	return "VolumeLabel"
}
type ExfatVolumeGuidDirectoryEntry struct {
	EntryType EntryType
	SecondaryCountRaw uint8
	SetChecksum uint16
	GeneralPrimaryFlags uint16
	VolumeGuid [16]byte
	Reserved [10]byte
}
func (vgde ExfatVolumeGuidDirectoryEntry) String() string {
	return fmt.Sprintf("VolumeGuidDirectoryEntry<SECONDARY-COUNT=(%d) SET-CHECKSUM=(0x%04x) GENERAL-PRIMARY-FLAGS=(0x%04x) GUID=[0x%016x...]>", vgde.SecondaryCountRaw, vgde.SetChecksum, vgde.GeneralPrimaryFlags, vgde.VolumeGuid[:4])
}
func (vgde ExfatVolumeGuidDirectoryEntry) SecondaryCount() uint8 {
	return vgde.SecondaryCountRaw
}
func (ExfatVolumeGuidDirectoryEntry) TypeName() string {
	return "VolumeGuid"
}
type ExfatTexFATDirectoryEntry struct {
	Reserved [32]byte
}
func (ExfatTexFATDirectoryEntry) String() string {
	return "TexFATDirectoryEntry<>"
}
func (ExfatTexFATDirectoryEntry) TypeName() string {
	return "TexFAT"
}
type GeneralSecondaryFlags uint8
func (gsf GeneralSecondaryFlags) IsAllocationPossible() bool {
	return gsf&1 > 0
}
func (gsf GeneralSecondaryFlags) NoFatChain() bool {
	return gsf&2 > 0
}