{-# LANGUAGE ForeignFunctionInterface #-}

{-|
Description:    Values related to on-disc data layout: track boundaries, sector sizes, address formats, etc.

Copyright:      (c) 2018-2021 Sam May
License:        GPL-3.0-or-later
Maintainer:     ag@eitilt.life

Stability:      stable
Portability:    non-portable (requires libcdio)

The bulk of this module deals with the sizes of various parts of disc sectors,
and so a brief description of each is likely helpful; for more information, see
the libcdio user guide, or any of the other various documentation that people
have written.  Depending on the accuracy required, multiple methods of encoding
data onto discs were developed enabling different degrees of error detection
and recovery.  For ease of implementation, these check regions are spaced
evenly throughout the data, effectively dividing it into blocks of 'sectorSize'
bytes.  The inner structure of each block is thus:

= Sector layout

== Audio

As audio CDs are intended to be listened to by human ears, often in noisy
environments, byte-perfect fidelity was deemed unnecessary; data fills the
entire sector without leaving room for checksums or synchronization points.
This structure is more technically described as "CD-DA" (Digital Audio).

@
|- 'sectorSize' -|
@

== Data (Mode 1)

The original structure described in the /Yellow Book/ standard.  Mode 1, Form 1
is notably the layout used for CD-ROMs.  It would be most correct to describe
these as "Mode 1" and "Mode 2" with no reference to form, but the libcdio
documentation has adopted the practice below as a parallel to the XA names.

=== Mode 1, Form 1

@
|------------------------------------ 'sectorSize' -------------------------------------|
 'syncSize' + 'headerSize' + __'dataSize'__ + 'errorDetectionSize' + 'padSize' + 'errorCorrectionSize'
           |------------------------------- 'syncedSize' -------------------------------|
                                   |-------------------- 'tailSize' --------------------|
@

=== Mode 1, Form 2

@
|------------ 'sectorSize' -----------|
 'syncSize' + 'headerSize' + __'dataSizeRaw'__
           |------ 'syncedSize' ------|
@


== XA (Mode 2)

Developed as a later extension to the Mode 1, Form 2 (technically just "Mode
2") standard above to allow different data to be interleaved.

=== Mode 2, Form 1

@
|--------------------------------------- 'sectorSize' ----------------------------------------|
 'syncSize' + 'headerSize' + 'subheaderSize' + __'dataSize'__ + 'errorDetectionSize' + 'errorCorrectionSize'
|---------- 'syncHeaderSizeXa' ---------|            |-------------- 'tailSizeXa' --------------|
           |------ 'headerSizeXa' ------|
           |---------------------------------- 'syncedSize' ----------------------------------|
                        |--------------------------- 'dataSizeRaw' ---------------------------|
@

=== Mode 2, Form 2

@
|--------------------------- 'sectorSize' --------------------------|
 'syncSize' + 'headerSize' + 'subheaderSize' + __'dataSizeRawXa'__ + 'padSizeXa'
|---------- 'syncHeaderSizeXa' ---------|
           |------ 'headerSizeXa' ------|
           |--------------------- 'syncedSize' ---------------------|
                        |-------------- 'dataSizeRaw' --------------|
                        |----- 'taggedDataSizeRaw' -----|
@


= @sector.h@

== Defines
* @CDIO_CD_CHUNK_SIZE@              -> 'chunkSize'
* @CDIO_CD_ECC_SIZE@                -> 'errorCorrectionSize'
* @CDIO_CD_EDC_SIZE@                -> 'errorDetectionSize'
* @CDIO_CD_FRAMESIZE@               -> 'dataSize'
* @CDIO_CD_FRAMESIZE_RAW@           -> 'sectorSize'
* @CDIO_CD_FRAMESIZE_RAW0@          -> 'dataSizeRaw'
* @CDIO_CD_FRAMESIZE_RAW1@          -> 'syncedSize'
* @CDIO_CD_FRAMESIZE_RAWER@         -> 'sectorSizeMax'
* @CDIO_CD_FRAMESIZE_SUB@           -> 'framesizeSub'
* @CDIO_CD_FRAMES_PER_MIN@          -> 'framesPerMin'
* @CDIO_CD_FRAMES_PER_SEC@          -> 'framesPerSec'
* @CDIO_CD_HEADER_SIZE@             -> 'headerSize'
* @CDIO_CD_M1F1_ZERO_SIZE@          -> 'padSize'
* @CDIO_CD_MAX_LSN@                 (removed; identical to @'maxBound' :: 'Lsn'@)
* @CDIO_CD_MAX_SESSIONS@            -> 'maxSessions'
* @CDIO_CD_MINS@                    -> 'cdMins'
* @CDIO_CD_MIN_LSN@                 (removed; identical to @'minBound' :: 'Lsn'@)
* @CDIO_CD_MIN_SESSION_NO@          -> 'minSessionNo'
* @CDIO_CD_NUM_OF_CHUNKS@           -> 'chunksPerSector'
* @CDIO_CD_SECS_PER_MIN@            -> 'secsPerMin'
* @CDIO_CD_SUBHEADER_SIZE@          -> 'subheaderSize'
* @CDIO_CD_SYNC_SIZE@               -> 'syncSize'
* @CDIO_CD_XA_HEADER@               -> 'headerSizeXa'
* @CDIO_CD_XA_SYNC_HEADER@          -> 'syncHeaderSizeXa'
* @CDIO_CD_XA_TAIL@                 -> 'tailSizeXa'
* @CDIO_POSTGAP_SECTORS@            -> 'postgapSectors'
* @CDIO_PREGAP_SECTORS@             -> 'pregapSectors'
* @M2F2_SECTOR_SIZE@                -> 'dataSizeRawXa'
* @M2RAW_SECTOR_SIZE@               (removed; identical to 'dataSizeRaw')
* @M2SUB_SECTOR_SIZE@               -> 'taggedDataSizeRaw'
* @msf_t_SIZEOF@                    (removed; structural constant not required for Haskell)

== Types
* @cdio_cd_minutes_sectors@         (removed; most values were simple calculations on @CDIO_CD_FRAMES_PER_MIN@, which can be done manually)

    - @CDIO_CD_MAX_SECTORS@         -> 'maxSectors'

* @cdio_subchannel@                 -> 'SubchannelData'

    - @CDIO_SUBCHANNEL_SUBQ_DATA@   -> 'QChannelData'

* @flag_t@                          -> 'Flags' and 'Flag'

    - @SCMS@                        -> 'SerialCopyManagement'

== Symbols
* @CDIO_SECTOR_SYNC_HEADER@         -> 'sectorSyncHeader'
* @cdio_lba_to_lsn@                 -> 'lbaToLsn'
* @cdio_lba_to_msf@                 -> 'lbaToMsf'
* @cdio_lba_to_msf_str@             -> 'lbaToMsfStr'
* @cdio_lsn_to_lba@                 -> 'lsnToLba'
* @cdio_lsn_to_msf@                 -> 'lsnToMsf'
* @cdio_msf_to_lba@                 -> 'msfToLba'
* @cdio_msf_to_lsn@                 -> 'msfToLsn'
* @cdio_msf_to_str@                 -> 'msfToStr'


= "Sound.Libcdio.Read.Data"

* 'Flag'                            (currently removed; unused in the translated portion of the interface)
* 'Flags'                           (currently removed; unused in the translated portion of the interface)
* 'Lba'                             (removed; only one address format is needed, and 'Lsn' is more widespread)
* 'Msf'                             (removed; only one address format is needed, and timestamps can be confusing for non-audio data)
* 'SubchannelData'                  (currently removed; unused in the translated portion of the interface)

* 'cdMins'                          -> 'Sound.Libcdio.Read.Data.maxCdMinutes'
* 'chunkSize'                       (removed; unnecessary low-level detail)
* 'chunksPerSector'                 (removed; unnecessary low-level detail)
* 'framesizeSub'                    (removed; unnecessary low-level detail)
* 'lbaToLsn'                        (removed; only 'Lsn' is used in @Sound@)
* 'lbaToMsf'                        (removed; only 'Lsn' is used in @Sound@)
* 'lbaToMsfStr'                     -> 'Sound.Libcdio.Read.Data.audioTimestamp'
* 'lsnToLba'                        (removed; only 'Lsn' is used in @Sound@)
* 'lsnToMsf'                        (removed; only 'Lsn' is used in @Sound@)
* 'maxSectors'                      -> 'Sound.Libcdio.Read.Data.maxSectors'
* 'maxSessions'                     (removed; explicit session handling not yet implemented by libcdio)
* 'minSessionNo'                    (removed; explicit session handling not yet implemented by libcdio)
* 'msfToLba'                        (removed; only 'Lsn' is used in @Sound@)
* 'msfToLsn'                        (removed; only 'Lsn' is used in @Sound@)
* 'msfToStr'                        (removed; only 'Lsn' is used in @Sound@)
* 'postgapSectors'                  -> 'Sound.Libcdio.Read.Data.defaultPostgapSectors'
* 'pregapSectors'                   -> 'Sound.Libcdio.Read.Data.defaultPregapSectors'
* 'secsPerMin'                      (removed; real-world constant)
* 'sectorSyncHeader'                (removed; unnecessary low-level detail)
* 'framesPerMin'                    (removed; simple arithmatic of 'framesPerSec'

All size constants (e.g. 'sectorSize') likewise removed as unnecessary.
-}
module Foreign.Libcdio.Sector
    ( -- * Types
      SubchannelData ( .. )
    , Flags
    , Flag ( .. )
    , Lba
    , Lsn
    , Msf ( .. )
      -- * Conversions
    , lbaToLsn
    , lsnToLba
    , lbaToMsf
    , msfToLba
    , lsnToMsf
    , msfToLsn
    , lbaToMsfStr
    , msfToStr
      -- * Basic counts
    , chunkSize
    , minSessionNo, maxSessions
    , pregapSectors, postgapSectors
    , maxSectors
    , framesizeSub
      -- ** Units of time
    , chunksPerSector
    , framesPerSec
    , secsPerMin
    , framesPerMin
    , cdMins
      -- * Sector blocks
    , sectorSize
    , syncedSize
    , sectorSizeMax
      -- ** Headers
    , sectorSyncHeader
    , syncSize
    , headerSize
    , subheaderSize
    , headerSizeXa
    , syncHeaderSizeXa
      -- ** Data
    , dataSize
    , dataSizeRaw
    , dataSizeRawXa
    , taggedDataSizeRaw
      -- ** Error correction
    , errorDetectionSize
    , errorCorrectionSize
    , padSize
    , padSizeXa
    , tailSize
    , tailSizeXa
    ) where


import qualified Data.Array.BitArray as A
import qualified Data.ByteString as BS

import qualified Foreign.C.String as C
import qualified Foreign.C.Types as C
import qualified Foreign.Ptr as C

import qualified Foreign.Marshal.Alloc as M
import qualified Foreign.Marshal.Utils as M
import qualified Foreign.Storable as S

import qualified System.IO.Unsafe as IO.Unsafe


import Foreign.Libcdio.Marshal
import Foreign.Libcdio.Types.Enums
import Foreign.Libcdio.Types.Internal
import Foreign.Libcdio.Types.Offsets
import Foreign.Libcdio.Util


-- | The collection of metadata describing a particular track.
type Flags = A.BitArray Flag


-- | The number of sectors spanned by a track pre-gap by default.
pregapSectors :: Word
pregapSectors :: Word
pregapSectors = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
pregapSectors'

foreign import ccall safe "cdio/compat/sector.h pregap_sectors"
  pregapSectors' :: C.CUInt

-- | The number of sectors spanned by a track post-gap by default.
postgapSectors :: Word
postgapSectors :: Word
postgapSectors = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
postgapSectors'

foreign import ccall safe "cdio/compat/sector.h postgap_sectors"
  postgapSectors' :: C.CUInt


-- | The typical maximum length of a disc, though it's not a strict limit.
cdMins :: Word
cdMins :: Word
cdMins = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
cdMins'

foreign import ccall safe "cdio/compat/sector.h cd_mins"
  cdMins' :: C.CUInt

-- | The number of seconds in a minute.
secsPerMin :: Word
secsPerMin :: Word
secsPerMin = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
secsPerMin'

foreign import ccall safe "cdio/compat/sector.h secs_per_min"
  secsPerMin' :: C.CUInt

-- | The number of disc sectors comprising a second of audio data.
framesPerSec :: Word
framesPerSec :: Word
framesPerSec = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
framesPerSec'

foreign import ccall safe "cdio/compat/sector.h frames_per_sec"
  framesPerSec' :: C.CUInt

-- | The number of bytes in a disc sector's sync header.
syncSize :: Word
syncSize :: Word
syncSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
syncSize'

foreign import ccall safe "cdio/compat/sector.h sync_size"
  syncSize' :: C.CUInt

-- | The size of the smallest meaningful segment of data, in bytes.
chunkSize :: Word
chunkSize :: Word
chunkSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
chunkSize'

foreign import ccall safe "cdio/compat/sector.h chunk_size"
  chunkSize' :: C.CUInt

-- | The number of data chunks comprising a disc sector.
chunksPerSector :: Word
chunksPerSector :: Word
chunksPerSector = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
chunksPerSector'

foreign import ccall safe "cdio/compat/sector.h chunks_per_frame"
  chunksPerSector' :: C.CUInt

-- | The size of a segment of data in the subchannel, in bytes.
framesizeSub :: Word
framesizeSub :: Word
framesizeSub = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
framesizeSub'

foreign import ccall safe "cdio/compat/sector.h framesize_sub"
  framesizeSub' :: C.CUInt


-- | The size of the address of a data sector, in bytes.
headerSize :: Word
headerSize :: Word
headerSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
headerSize'

foreign import ccall safe "cdio/compat/sector.h header_size"
  headerSize' :: C.CUInt

-- | The size of the subheader of an XA sector, in bytes.
subheaderSize :: Word
subheaderSize :: Word
subheaderSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
subheaderSize'

foreign import ccall safe "cdio/compat/sector.h subheader_size"
  subheaderSize' :: C.CUInt

-- | The size of the EDC error correction segment, in bytes.
errorDetectionSize :: Word
errorDetectionSize :: Word
errorDetectionSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
errorDetectionSize'

foreign import ccall safe "cdio/compat/sector.h edc_size"
  errorDetectionSize' :: C.CUInt

-- | The amount of padding between the EDC and ECC segments in a Mode 1 sector,
-- in bytes.
padSize :: Word
padSize :: Word
padSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
padSize'

foreign import ccall safe "cdio/compat/sector.h mode1_pad_size"
  padSize' :: C.CUInt

-- | The size of the ECC error correction segment, in bytes.
errorCorrectionSize :: Word
errorCorrectionSize :: Word
errorCorrectionSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
errorCorrectionSize'

foreign import ccall safe "cdio/compat/sector.h ecc_size"
  errorCorrectionSize' :: C.CUInt


-- | The amount of data which may be contained in a disc sector /with/ error
-- correction, in bytes.
dataSize :: Word
dataSize :: Word
dataSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
dataSize'

foreign import ccall safe "cdio/compat/sector.h framesize"
  dataSize' :: C.CUInt

-- | The size of an entire disc sector, in bytes.
sectorSize :: Word
sectorSize :: Word
sectorSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
sectorSize'

foreign import ccall safe "cdio/compat/sector.h framesize_raw"
  sectorSize' :: C.CUInt

-- | The maximum number of bytes that may be returned from a single call.
sectorSizeMax :: Word
sectorSizeMax :: Word
sectorSizeMax = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
sectorSizeMax'

foreign import ccall safe "cdio/compat/sector.h framesize_rawer"
  sectorSizeMax' :: C.CUInt

-- | The size of a disc sector in bytes, ignoring the sync header.
syncedSize :: Word
syncedSize :: Word
syncedSize = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
syncedSize'

foreign import ccall safe "cdio/compat/sector.h framesize_raw1"
  syncedSize' :: C.CUInt

-- | The amount of data which may be contained in a disc sector /without/ error
-- correction, in bytes.
dataSizeRaw :: Word
dataSizeRaw :: Word
dataSizeRaw = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
dataSizeRaw'

foreign import ccall safe "cdio/compat/sector.h framesize_raw0"
  dataSizeRaw' :: C.CUInt


-- | The total size of the meaningful XA sector headers, in bytes.
headerSizeXa :: Word
headerSizeXa :: Word
headerSizeXa = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
headerSizeXa'

foreign import ccall safe "cdio/compat/sector.h header_size_xa"
  headerSizeXa' :: C.CUInt

-- | The total size of all data sector error correction segments, in bytes.
tailSize :: Word
tailSize :: Word
tailSize = Word
errorDetectionSize Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word
padSize Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word
errorCorrectionSize

-- | The total size of all XA sector error correction segments, in bytes.
tailSizeXa :: Word
tailSizeXa :: Word
tailSizeXa = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
tailSizeXa'

foreign import ccall safe "cdio/compat/sector.h tail_size_xa"
  tailSizeXa' :: C.CUInt

-- | The total size of all headers in an XA sector, including the sync marker.
syncHeaderSizeXa :: Word
syncHeaderSizeXa :: Word
syncHeaderSizeXa = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
syncHeaderSizeXa'

foreign import ccall safe "cdio/compat/sector.h sync_size_xa"
  syncHeaderSizeXa' :: C.CUInt

-- | The amount of padding at the end of an XA sector without error correction,
-- in bytes.
padSizeXa :: Word
padSizeXa :: Word
padSizeXa = Word
dataSizeRaw Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
taggedDataSizeRaw


-- | The byte sequence used to mark the start of a disc sector, to allow
-- correcting drift while reading.
sectorSyncHeader :: BS.ByteString
sectorSyncHeader :: ByteString
sectorSyncHeader = IO ByteString -> ByteString
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO ByteString -> ByteString) -> IO ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$
    CStringLen -> IO ByteString
BS.packCStringLen (Ptr CChar
sectorSyncHeader', Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
syncSize)

foreign import ccall safe "cdio/compat/sector.h sector_sync_header"
  sectorSyncHeader' :: C.Ptr C.CChar


-- | The amount of data contained in an XA sector without error correction, in
-- bytes.
dataSizeRawXa :: Word
dataSizeRawXa :: Word
dataSizeRawXa = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
dataSizeRawXa'

foreign import ccall safe "cdio/compat/sector.h data_size_xa"
  dataSizeRawXa' :: C.CUInt

-- | The size of tagged data (counting the subheader) contained in an XA sector
-- without error correction, in bytes.
taggedDataSizeRaw :: Word
taggedDataSizeRaw :: Word
taggedDataSizeRaw = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
taggedDataSizeRaw'

foreign import ccall safe "cdio/compat/sector.h tagged_size_xa"
  taggedDataSizeRaw' :: C.CUInt


-- | How many sessions are allowed on a disc.
maxSessions :: Word
maxSessions :: Word
maxSessions = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
maxSessions'

foreign import ccall safe "cdio/compat/sector.h max_session"
  maxSessions' :: C.CUInt

-- | The smallest session number on a disc.
minSessionNo :: Word
minSessionNo :: Word
minSessionNo = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
minSessionNo'

foreign import ccall safe "cdio/compat/sector.h min_session"
  minSessionNo' :: C.CUInt


-- | A shortcut for calculating @'framesPerSec' * 'secsPerMin'@
framesPerMin :: Word
framesPerMin :: Word
framesPerMin = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
framesPerMin'

foreign import ccall safe "cdio/compat/sector.h frames_per_min"
  framesPerMin' :: C.CUInt


-- | The maximum number of sectors allowed to be stored on a disc.
maxSectors :: Word
maxSectors :: Word
maxSectors = CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
maxSectors'

foreign import ccall safe "cdio/compat/sector.h max_sectors"
  maxSectors' :: C.CUInt


-- | Minute\/second\/frame structure for addresses.  Generally only makes sense
-- for audio discs.
data Msf = Msf
    { Msf -> Word
minute :: Word
    , Msf -> Word
second :: Word
        -- ^ 'secsPerMin'
    , Msf -> Word
frame :: Word
        -- ^ 'framesPerSec'
    }
  deriving ( Int -> Msf -> ShowS
[Msf] -> ShowS
Msf -> String
(Int -> Msf -> ShowS)
-> (Msf -> String) -> ([Msf] -> ShowS) -> Show Msf
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Msf] -> ShowS
$cshowList :: [Msf] -> ShowS
show :: Msf -> String
$cshow :: Msf -> String
showsPrec :: Int -> Msf -> ShowS
$cshowsPrec :: Int -> Msf -> ShowS
Show, ReadPrec [Msf]
ReadPrec Msf
Int -> ReadS Msf
ReadS [Msf]
(Int -> ReadS Msf)
-> ReadS [Msf] -> ReadPrec Msf -> ReadPrec [Msf] -> Read Msf
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Msf]
$creadListPrec :: ReadPrec [Msf]
readPrec :: ReadPrec Msf
$creadPrec :: ReadPrec Msf
readList :: ReadS [Msf]
$creadList :: ReadS [Msf]
readsPrec :: Int -> ReadS Msf
$creadsPrec :: Int -> ReadS Msf
Read )
instance Eq Msf where
    Msf
l == :: Msf -> Msf -> Bool
== Msf
r = Msf -> Msf -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Msf
l Msf
r Ordering -> Ordering -> Bool
forall a. Eq a => a -> a -> Bool
== Ordering
EQ
instance Ord Msf where
    compare :: Msf -> Msf -> Ordering
compare Msf
l Msf
r = case Msf -> Word
minute Msf
l' Word -> Word -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` Msf -> Word
minute Msf
r' of
        Ordering
EQ -> case Msf -> Word
second Msf
l' Word -> Word -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` Msf -> Word
second Msf
r' of
            Ordering
EQ -> Msf -> Word
frame Msf
l' Word -> Word -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` Msf -> Word
frame Msf
r'
            Ordering
x -> Ordering
x
        Ordering
x -> Ordering
x
      where l' :: Msf
l' = Msf -> Msf
normalize Msf
l
            r' :: Msf
r' = Msf -> Msf
normalize Msf
r
            normalize :: Msf -> Msf
normalize msf :: Msf
msf@(Msf Word
m Word
s Word
f)
                | Word
f Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
>= Word
framesPerSec = Msf -> Msf
normalize (Msf -> Msf) -> Msf -> Msf
forall a b. (a -> b) -> a -> b
$ Word -> Word -> Word -> Msf
Msf Word
m (Word
s Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word -> Word -> Word
forall a. Integral a => a -> a -> a
div Word
f Word
framesPerSec) (Word -> Word -> Word
forall a. Integral a => a -> a -> a
mod Word
f Word
framesPerSec)
                | Word
s Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
>= Word
secsPerMin = Word -> Word -> Word -> Msf
Msf (Word
m Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word -> Word -> Word
forall a. Integral a => a -> a -> a
div Word
s Word
secsPerMin) (Word -> Word -> Word
forall a. Integral a => a -> a -> a
mod Word
s Word
secsPerMin) Word
f
                | Bool
otherwise = Msf
msf
instance Bounded Msf where
    minBound :: Msf
minBound = Word -> Word -> Word -> Msf
Msf Word
0 Word
0 Word
0
    maxBound :: Msf
maxBound = Word -> Word -> Word -> Msf
Msf Word
forall a. Bounded a => a
maxBound (Word
secsPerMin Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
1) (Word
framesPerSec Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
1)
instance S.Storable Msf where
    sizeOf :: Msf -> Int
sizeOf Msf
_    = Int
msfSizeOf
    alignment :: Msf -> Int
alignment Msf
_ = Int
msfAlign
    peek :: Ptr Msf -> IO Msf
peek Ptr Msf
c = do
        Bcd
m <- Ptr Msf -> Int -> IO Bcd
forall a b. Storable a => Ptr b -> Int -> IO a
S.peekByteOff Ptr Msf
c Int
msfM
        Bcd
s <- Ptr Msf -> Int -> IO Bcd
forall a b. Storable a => Ptr b -> Int -> IO a
S.peekByteOff Ptr Msf
c Int
msfS
        Bcd
f <- Ptr Msf -> Int -> IO Bcd
forall a b. Storable a => Ptr b -> Int -> IO a
S.peekByteOff Ptr Msf
c Int
msfF
        Msf -> IO Msf
forall (m :: * -> *) a. Monad m => a -> m a
return (Msf -> IO Msf) -> Msf -> IO Msf
forall a b. (a -> b) -> a -> b
$ Word -> Word -> Word -> Msf
Msf (Bcd -> Word
fromBcd8 Bcd
m) (Bcd -> Word
fromBcd8 Bcd
s) (Bcd -> Word
fromBcd8 Bcd
f)
    poke :: Ptr Msf -> Msf -> IO ()
poke Ptr Msf
c Msf
hs = do
        Ptr Msf -> Int -> Bcd -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
S.pokeByteOff Ptr Msf
c Int
msfM (Bcd -> IO ()) -> (Word -> Bcd) -> Word -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word -> Bcd
toBcd8 (Word -> IO ()) -> Word -> IO ()
forall a b. (a -> b) -> a -> b
$ Msf -> Word
minute Msf
hs
        Ptr Msf -> Int -> Bcd -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
S.pokeByteOff Ptr Msf
c Int
msfS (Bcd -> IO ()) -> (Word -> Bcd) -> Word -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word -> Bcd
toBcd8 (Word -> IO ()) -> Word -> IO ()
forall a b. (a -> b) -> a -> b
$ Msf -> Word
second Msf
hs
        Ptr Msf -> Int -> Bcd -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
S.pokeByteOff Ptr Msf
c Int
msfF (Bcd -> IO ()) -> (Word -> Bcd) -> Word -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word -> Bcd
toBcd8 (Word -> IO ()) -> Word -> IO ()
forall a b. (a -> b) -> a -> b
$ Msf -> Word
frame Msf
hs
instance Enum Msf where
    fromEnum :: Msf -> Int
fromEnum Msf
msf = Integer -> Int
forall a. Num a => Integer -> a
fromInteger
        (Integer -> Int) -> Integer -> Int
forall a b. (a -> b) -> a -> b
$ Word -> Integer
forall a. Integral a => a -> Integer
toInteger (Msf -> Word
frame Msf
msf)
        Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Word -> Integer
forall a. Integral a => a -> Integer
toInteger (Msf -> Word
second Msf
msf) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Word -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
framesPerSec
        Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Word -> Integer
forall a. Integral a => a -> Integer
toInteger (Msf -> Word
minute Msf
msf) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Word -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
framesPerMin
    toEnum :: Int -> Msf
toEnum Int
i = Msf :: Word -> Word -> Word -> Msf
Msf
        { minute :: Word
minute = Word -> Word -> Word
forall a. Integral a => a -> a -> a
div Word
i' Word
framesPerMin
        , second :: Word
second = (Word -> Word -> Word) -> Word -> Word -> Word
forall a b c. (a -> b -> c) -> b -> a -> c
flip Word -> Word -> Word
forall a. Integral a => a -> a -> a
mod Word
secsPerMin (Word -> Word) -> Word -> Word
forall a b. (a -> b) -> a -> b
$ Word -> Word -> Word
forall a. Integral a => a -> a -> a
div Word
i' Word
framesPerSec
        , frame :: Word
frame = Word -> Word -> Word
forall a. Integral a => a -> a -> a
mod Word
i' Word
framesPerSec
        }
      where i' :: Word
i' = Int -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
i
    pred :: Msf -> Msf
pred (Msf Word
0 Word
0 Word
0) = String -> Msf
forall a. HasCallStack => String -> a
error String
"Enum.pred(Msf): tried to take `pred' of minBound"
    pred (Msf Word
m Word
s Word
f)
        | Word
f Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
>= Word
framesPerSec = Msf -> Msf
forall a. Enum a => a -> a
pred (Msf -> Msf) -> Msf -> Msf
forall a b. (a -> b) -> a -> b
$ Word -> Word -> Word -> Msf
Msf Word
m (Word
s Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word -> Word -> Word
forall a. Integral a => a -> a -> a
div Word
f Word
framesPerSec) (Word -> Word -> Word
forall a. Integral a => a -> a -> a
mod Word
f Word
framesPerSec)
        | Word
s Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
>= Word
secsPerMin = Msf -> Msf
forall a. Enum a => a -> a
pred (Msf -> Msf) -> Msf -> Msf
forall a b. (a -> b) -> a -> b
$ Word -> Word -> Word -> Msf
Msf (Word
m Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word -> Word -> Word
forall a. Integral a => a -> a -> a
div Word
s Word
secsPerMin) (Word -> Word -> Word
forall a. Integral a => a -> a -> a
mod Word
s Word
secsPerMin) Word
f
        | Word
f Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0 Bool -> Bool -> Bool
&& Word
s Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0 = Word -> Word -> Word -> Msf
Msf (Word
m Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
1) (Word
secsPerMin Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
1) (Word
framesPerSec Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
1)
        | Word
f Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0 = Word -> Word -> Word -> Msf
Msf Word
m (Word
s Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
1) (Word
framesPerSec Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
1)
        | Bool
otherwise = Word -> Word -> Word -> Msf
Msf Word
m Word
s (Word
f Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
1)
    succ :: Msf -> Msf
succ msf :: Msf
msf@(Msf Word
m Word
s Word
f)
        | Msf
msf Msf -> Msf -> Bool
forall a. Ord a => a -> a -> Bool
>= Msf
forall a. Bounded a => a
maxBound = String -> Msf
forall a. HasCallStack => String -> a
error String
"Enum.succ(Msf): tried to take `succ' of maxBound"
        | Bool
otherwise = let (Word
fd, Word
fm) = Word -> Word -> Word -> (Word, Word)
forall a. (Bounded a, Integral a) => a -> a -> a -> (a, a)
overflow Word
f Word
1 Word
framesPerSec
                          (Word
sd, Word
sm) = Word -> Word -> Word -> (Word, Word)
forall a. (Bounded a, Integral a) => a -> a -> a -> (a, a)
overflow Word
s Word
fd Word
secsPerMin
                      in Word -> Word -> Word -> Msf
Msf (Word
m Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word
sd) Word
sm Word
fm
      where -- Relies on unsigned type, and @y + b < maxBound@
            overflow :: a -> a -> a -> (a, a)
overflow a
x a
y a
b
                | a
x a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
forall a. Bounded a => a
maxBound a -> a -> a
forall a. Num a => a -> a -> a
- a
y = (a -> a -> a
forall a. Integral a => a -> a -> a
div (a
x a -> a -> a
forall a. Num a => a -> a -> a
- a
b a -> a -> a
forall a. Num a => a -> a -> a
+ a
y) a
b a -> a -> a
forall a. Num a => a -> a -> a
+ a
1, a -> a -> a
forall a. Integral a => a -> a -> a
mod (a
x a -> a -> a
forall a. Num a => a -> a -> a
- a
b a -> a -> a
forall a. Num a => a -> a -> a
+ a
y) a
b)
                | Bool
otherwise = (a -> a -> a
forall a. Integral a => a -> a -> a
div (a
x a -> a -> a
forall a. Num a => a -> a -> a
+ a
y) a
b, a -> a -> a
forall a. Integral a => a -> a -> a
mod (a
x a -> a -> a
forall a. Num a => a -> a -> a
+ a
y) a
b)

{- Use @sizeOf Msf@
msfSizeof :: Word
msfSizeof = fromIntegral msfSizeof'

foreign import ccall safe "cdio/compat/sector.h sizeof_msf_const"
  msfSizeof' :: C.CSize
-}


{- Never used after definition.
-- | Address in one of the two primary address formats.
data CdromAddr
    = MsfAddr Msf
    | LbaAddr Lba
  deriving ( Eq, Show, Read, Ord )
-- | Note that, while the 'peek' implementation attempts to detect which form
-- the address takes, it may get it wrong and read a 'Msf' address as an 'Lba'
-- or vice versa.  If the type is otherwise known, it may be better to read
-- the pointer as that type directly, and wrap into the union manually.
instance S.Storable CdromAddr where
    sizeOf _    = adrSizeOf
    alignment _ = adrAlign
    peek c = do
        msf <- S.peek $ C.castPtr c
        lba <- S.peek $ C.castPtr c
        -- Barring uncleared bits, an LBA has an extra byte.
        if mod 0x100 lba /= 0
           || second msf >= secsPerMin
           || frame msf >= framesPerSec
        then return $ LbaAddr lba
        else return $ MsfAddr msf
    poke c (MsfAddr hs) = S.poke (C.castPtr c) hs
    poke c (LbaAddr hs) = S.poke (C.castPtr c) hs
-}


-- | Print a logical address as the corresponding timestamp, assuming audio
-- data.
lbaToMsfStr :: Lba -> String
lbaToMsfStr :: Lba -> String
lbaToMsfStr Lba
l = IO String -> String
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO String -> String) -> IO String -> String
forall a b. (a -> b) -> a -> b
$
    CLba -> IO (Ptr CChar)
lbaToMsfStr' (Lba -> CLba
forall a b. (Integral a, Num b) => a -> b
fromIntegral Lba
l) IO (Ptr CChar) -> (Ptr CChar -> IO String) -> IO String
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Ptr CChar -> IO String
peekFString

foreign import ccall safe "cdio/compat/sector.h cdio_lba_to_msf_str"
  lbaToMsfStr' :: CLba -> IO C.CString

-- | Print a disc timestamp in the standard "MM:SS:FF" format.
msfToStr :: Msf -> String
msfToStr :: Msf -> String
msfToStr Msf
msf = IO String -> String
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO String -> String) -> IO String -> String
forall a b. (a -> b) -> a -> b
$
    Msf -> (Ptr Msf -> IO (Ptr CChar)) -> IO (Ptr CChar)
forall a b. Storable a => a -> (Ptr a -> IO b) -> IO b
M.with Msf
msf Ptr Msf -> IO (Ptr CChar)
msfToStr' IO (Ptr CChar) -> (Ptr CChar -> IO String) -> IO String
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Ptr CChar -> IO String
C.peekCString

foreign import ccall safe "cdio/compat/sector.h cdio_msf_to_str"
  msfToStr' :: C.Ptr Msf -> IO C.CString

-- | Convert an LBA address into the corresponding LSN.
lbaToLsn :: Lba -> Lsn
lbaToLsn :: Lba -> Lsn
lbaToLsn = CLba -> Lsn
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CLba -> Lsn) -> (Lba -> CLba) -> Lba -> Lsn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CLba -> CLba
lbaToLsn' (CLba -> CLba) -> (Lba -> CLba) -> Lba -> CLba
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Lba -> CLba
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- C erroneously returns @lba_t@.
foreign import ccall safe "cdio/compat/sector.h cdio_lba_to_lsn"
  lbaToLsn' :: CLba -> CLba

-- | Convert an LBA address into the corresponding timestamp, assuming audio
-- data.
lbaToMsf :: Lba -> Msf
lbaToMsf :: Lba -> Msf
lbaToMsf Lba
l = IO Msf -> Msf
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO Msf -> Msf)
-> ((Ptr Msf -> IO Msf) -> IO Msf) -> (Ptr Msf -> IO Msf) -> Msf
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Ptr Msf -> IO Msf) -> IO Msf
forall a b. Storable a => (Ptr a -> IO b) -> IO b
M.alloca ((Ptr Msf -> IO Msf) -> Msf) -> (Ptr Msf -> IO Msf) -> Msf
forall a b. (a -> b) -> a -> b
$ \Ptr Msf
m' ->
    CLba -> Ptr Msf -> IO ()
lbaToMsf' (Lba -> CLba
forall a b. (Integral a, Num b) => a -> b
fromIntegral Lba
l) Ptr Msf
m' IO () -> IO Msf -> IO Msf
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr Msf -> IO Msf
forall a. Storable a => Ptr a -> IO a
S.peek Ptr Msf
m'

foreign import ccall safe "cdio/compat/sector.h cdio_lba_to_msf"
  lbaToMsf' :: CLba -> C.Ptr Msf -> IO ()

-- | Convert an LSN address into the corresponding LBA.
lsnToLba :: Lsn -> Lba
lsnToLba :: Lsn -> Lba
lsnToLba = CLba -> Lba
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CLba -> Lba) -> (Lsn -> CLba) -> Lsn -> Lba
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CLba -> CLba
lsnToLba' (CLba -> CLba) -> (Lsn -> CLba) -> Lsn -> CLba
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Lsn -> CLba
forall a b. (Integral a, Num b) => a -> b
fromIntegral

foreign import ccall safe "cdio/compat/sector.h cdio_lsn_to_lba"
  lsnToLba' :: CLsn -> CLba

-- | Convert an LSN address into the corresponding timestamp, assuming audio
-- data.
lsnToMsf :: Lsn -> Msf
lsnToMsf :: Lsn -> Msf
lsnToMsf Lsn
l = IO Msf -> Msf
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO Msf -> Msf)
-> ((Ptr Msf -> IO Msf) -> IO Msf) -> (Ptr Msf -> IO Msf) -> Msf
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Ptr Msf -> IO Msf) -> IO Msf
forall a b. Storable a => (Ptr a -> IO b) -> IO b
M.alloca ((Ptr Msf -> IO Msf) -> Msf) -> (Ptr Msf -> IO Msf) -> Msf
forall a b. (a -> b) -> a -> b
$ \Ptr Msf
m' ->
    CLba -> Ptr Msf -> IO ()
lsnToMsf' (Lsn -> CLba
forall a b. (Integral a, Num b) => a -> b
fromIntegral Lsn
l) Ptr Msf
m' IO () -> IO Msf -> IO Msf
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr Msf -> IO Msf
forall a. Storable a => Ptr a -> IO a
S.peek Ptr Msf
m'

foreign import ccall safe "cdio/compat/sector.h cdio_lsn_to_msf"
  lsnToMsf' :: CLsn -> C.Ptr Msf -> IO ()

-- | Convert a timestamp into the corresponding LBA address, assuming audio
-- data.
msfToLba :: Msf -> Lba
msfToLba :: Msf -> Lba
msfToLba = CLba -> Lba
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CLba -> Lba) -> (Msf -> CLba) -> Msf -> Lba
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO CLba -> CLba
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO CLba -> CLba) -> (Msf -> IO CLba) -> Msf -> CLba
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Msf -> (Ptr Msf -> IO CLba) -> IO CLba)
-> (Ptr Msf -> IO CLba) -> Msf -> IO CLba
forall a b c. (a -> b -> c) -> b -> a -> c
flip Msf -> (Ptr Msf -> IO CLba) -> IO CLba
forall a b. Storable a => a -> (Ptr a -> IO b) -> IO b
M.with Ptr Msf -> IO CLba
msfToLba'

foreign import ccall safe "cdio/compat/sector.h cdio_msf_to_lba"
  msfToLba' :: C.Ptr Msf -> IO CLsn

-- | Convert a timestamp into the corresponding LSN address, assuming audio
-- data.
msfToLsn :: Msf -> Lsn
msfToLsn :: Msf -> Lsn
msfToLsn = CLba -> Lsn
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CLba -> Lsn) -> (Msf -> CLba) -> Msf -> Lsn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO CLba -> CLba
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO CLba -> CLba) -> (Msf -> IO CLba) -> Msf -> CLba
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Msf -> (Ptr Msf -> IO CLba) -> IO CLba)
-> (Ptr Msf -> IO CLba) -> Msf -> IO CLba
forall a b c. (a -> b -> c) -> b -> a -> c
flip Msf -> (Ptr Msf -> IO CLba) -> IO CLba
forall a b. Storable a => a -> (Ptr a -> IO b) -> IO b
M.with Ptr Msf -> IO CLba
msfToLsn'

foreign import ccall safe "cdio/compat/sector.h cdio_msf_to_lsn"
  msfToLsn' :: C.Ptr Msf -> IO CLsn

{- Not exported
-- | Convert a split timestamp into the corresponding LBA address, assuming
-- audio data.
msf3ToLba :: Word -> Word -> Word -> Lba
msf3ToLba m s f = fromIntegral $ msf3ToLba' (fromIntegral m) (fromIntegral s) (fromIntegral f)

foreign import ccall safe "cdio/compat/sector.h cdio_msf3_to_lba"
  msf3ToLba' :: C.CUInt -> C.CUInt -> C.CUInt -> CLba

-- | Read a timestamp in the form MM:SS:FF into the corresponding LBA address,
-- assuming audio data.
mmssffToLba :: String -> Maybe Lba
mmssffToLba s = invalidLba . IO.Unsafe.unsafePerformIO $ C.withCString s mmssffToLba

foreign import ccall safe "cdio/compat/sector.h cdio_mmssff_to_lba"
  mmssffToLba' :: C.CString -> IO CLba
-}