<?php
  
/*
   http://www.omnibase.net/smf/
   http://sfkonu.vni.jp/sbcsoft/mgame/mldtest.html
   */

require_once 'IO/Bit.php';

class 
IO_SMAF {
    var 
$headers = array();
    var 
$chunks = array();
    
    var 
$nestedChunkIDList = array(
        
// ChunkID => (Child) Data Structure Type
        
'OPDA' => 'Chunks'// Optional Data Chunk
        
'Dch' => 'Tags',
        
'MTR' => 'Chunks',
        );
    function 
nestedChunkIDSize($chunkID) {
        foreach (
$this->nestedChunkIDList as $id => $size) {
            if (
$id === substr($chunkID0strlen($id))) {
                return 
$size;
            }
        }
        return 
0;
    }
    function 
parse($smafdata) {
        
$reader = new IO_Bit();
        
$reader->input($smafdata);
        
$this->headers['ID'] = $reader->getData(4);
        
$size $reader->getUI32BE(4);
        
$this->headers['Size'] = $size;
        while (
$reader->hasNextData(8)) {
            
$this->chunks $this->parseDataChunks($reader);
        }
    }
    function 
seekToAlphabet(&$reader) {
        while (
true) {
            
$data $reader->getData(1);
            if (
ctype_alpha($data)) {
                break;
            }
        }
        
$reader->incrementOffset(-10);
    }

    function 
parseDataChunks(&$reader) {
        
$chunks = array();
        while (
$reader->hasNextData(8)) {
            
$chunk = array();
            
$chunkID $reader->getData(4);
            
$size $reader->getUI32BE();
            
$chunk['ID'] = $chunkID;
            
$chunk['Size'] = $size;
            switch (
substr($chunkID03)) {
              case 
'CNT'// CNTI: Contents Information Chunk
                
$chunk['Contents Class'] = $reader->getUI8();
                
$chunk['Contents Type'] = $reader->getUI8();
                
$chunk['Contents Code Type'] = $reader->getUI8();
                
$chunk['Copy Status'] = $reader->getUI8();
                
$chunk['Copy Counts'] = $reader->getUI8();
                if (
$size 5) {
                    
$chunk['Option'] = $reader->getData($size 5);
                }
                break;
            case 
'MTR'// MTR*: Score Track Chunk
                
$chunk['Format Type'] = $reader->getUI8();
                
$chunk['Sequence Type'] = $reader->getUI8();
                
$chunk['Timebase D'] = $reader->getUI8();
                
$chunk['Timebase G'] = $reader->getUI8();
                
$chunk['Channnel Status'] = $reader->getUI16BE();
                
$data $reader->getData($size 6);
                
$reader_chunkdata = new IO_Bit();
                
$reader_chunkdata->input($data);
                
$this->seekToAlphabet($reader_chunkdata);
                
$chunk[$structure_type] = $this->parseDataChunks($reader_chunkdata);
                break;
                
/*
            case 'Mts': // Mtsu, Mtsq, Mts
                $data = $reader->getData($size);
                $reader_tracks = new IO_Bit();
                $reader_tracks->input($data);
                switch ($chunkID) {
                case 'Mtsu': // Score Track Setup Data
                    break 2;
                case 'Mtsq': // Score Track Sequence Data
                    $tracks = array();
                    while ($reader_tracks->hasNextData(4)) {
                        $end_check = $reader_tracks->getUI32BE();
                        if ($end_check == 0) {
                            break;
                        }
                        $reader_tracks->incrementOffset(-4, 0);
                        $duration = $reader_tracks->getUI8();
                        if ($duration & 0x80) {
                            $duration = ($duration & 0x7F) << 8;
                            $duration |= $reader_tracks->getUI8();
                        }
                        $event = $reader_tracks->getUI8();
                        $track = array(
                            'Duration' => $duration,
                            'Event' => $event);
                                                
                        $tracks []= $track;
                    }
                    $chunk['Tracks'] = $tracks;
                    break 2;
                }
                */
              
default:
                
$data $reader->getData($size);
                if (
$structure_type $this->nestedChunkIDSize($chunkID)) {
                    
$reader_chunkdata = new IO_Bit();
                    
$reader_chunkdata->input($data);
                    
$method 'parseData'.$structure_type;
                    
$chunk[$structure_type] = $this->$method($reader_chunkdata);
                } else {
                    
$chunk['Data'] = $data;
                }
                break;
            }
//            var_dump($chunk);
            
$chunks []= $chunk;
        }
        return 
$chunks;
    }
    function 
parseDataTags(&$reader) {
        
$tags = array();
        while (
$reader->hasNextData(4)) {
            
$tag = array();
            
$tagID $reader->getData(2);
            
$size $reader->getUI16BE();
            
$tag['ID'] = $tagID;
            
$tag['Size'] = $size;
            
$tag['Data'] = $reader->getData($size);
//            var_dump($tag);
            
$tags []= $tag;
        }
        return 
$tags;
    }
    function 
dump() {
        
var_dump($this->headers);
        
var_dump($this->chunks);
    }
}