function ParseOptimFROGheader45(&$fd, &$ThisFileInfo)
 {
     // for fileformat of v4.50a and higher
     $RIFFdata = '';
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     while (!feof($fd) && ftell($fd) < $ThisFileInfo['avdataend']) {
         $BlockOffset = ftell($fd);
         $BlockData = fread($fd, 8);
         $offset = 8;
         $BlockName = substr($BlockData, 0, 4);
         $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
         if ($BlockName == 'OFRX') {
             $BlockName = 'OFR ';
         }
         if (!isset($ThisFileInfo['ofr'][$BlockName])) {
             $ThisFileInfo['ofr'][$BlockName] = array();
         }
         $thisfile_ofr_thisblock =& $ThisFileInfo['ofr'][$BlockName];
         switch ($BlockName) {
             case 'OFR ':
                 // shortcut
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha';
                 switch ($BlockSize) {
                     case 12:
                     case 15:
                         // good
                         break;
                     default:
                         $ThisFileInfo['warning'][] = '"' . $BlockName . '" contains more data than expected (expected 12 or 15 bytes, found ' . $BlockSize . ' bytes)';
                         break;
                 }
                 $BlockData .= fread($fd, $BlockSize);
                 $thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
                 $offset += 6;
                 $thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
                 $offset += 1;
                 $thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
                 $offset += 1;
                 $thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 if ($BlockSize > 12) {
                     // OFR 4.504b or higher
                     $thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
                     $thisfile_ofr_thisblock['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
                     $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
                     $offset += 2;
                     $thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
                     $thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
                     $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
                     $offset += 1;
                     $ThisFileInfo['audio']['encoder'] = 'OptimFROG ' . $thisfile_ofr_thisblock['encoder'];
                     $ThisFileInfo['audio']['encoder_options'] = '--mode ' . $thisfile_ofr_thisblock['compression'];
                     if (($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xf0) >> 4 == 7) {
                         // v4.507
                         if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') {
                             // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference
                             // between lossless and lossy other than the file extension.
                             $ThisFileInfo['audio']['dataformat'] = 'ofs';
                             $ThisFileInfo['audio']['lossless'] = true;
                         }
                     }
                 }
                 $ThisFileInfo['audio']['channels'] = $thisfile_ofr_thisblock['channels'];
                 $ThisFileInfo['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate'];
                 $ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
                 break;
             case 'COMP':
                 // unlike other block types, there CAN be multiple COMP blocks
                 $COMPdata['offset'] = $BlockOffset;
                 $COMPdata['size'] = $BlockSize;
                 if ($ThisFileInfo['avdataoffset'] == 0) {
                     $ThisFileInfo['avdataoffset'] = $BlockOffset;
                 }
                 // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
                 $BlockData .= fread($fd, 14);
                 fseek($fd, $BlockSize - 14, SEEK_CUR);
                 $COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 $COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 $COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
                 $offset += 1;
                 $COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
                 $offset += 1;
                 $COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
                 //$COMPdata['algorithm']                    = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
                 $offset += 2;
                 if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) {
                     // OFR 4.504b or higher
                     $COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
                     $COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
                     $offset += 2;
                 }
                 if ($COMPdata['crc_32'] == 0x454e4f4e) {
                     // ASCII value of 'NONE' - placeholder value in v4.50a
                     $COMPdata['crc_32'] = false;
                 }
                 $thisfile_ofr_thisblock[] = $COMPdata;
                 break;
             case 'HEAD':
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $RIFFdata .= fread($fd, $BlockSize);
                 break;
             case 'TAIL':
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 if ($BlockSize > 0) {
                     $RIFFdata .= fread($fd, $BlockSize);
                 }
                 break;
             case 'RECV':
                 // block contains no useful meta data - simply note and skip
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 fseek($fd, $BlockSize, SEEK_CUR);
                 break;
             case 'APET':
                 // APEtag v2
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version (' . GETID3_VERSION . ') of getID3()';
                 fseek($fd, $BlockSize, SEEK_CUR);
                 break;
             case 'MD5 ':
                 // APEtag v2
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 if ($BlockSize == 16) {
                     $thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize);
                     $thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
                     $ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
                 } else {
                     $ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found ' . $BlockSize . ' instead';
                     fseek($fd, $BlockSize, SEEK_CUR);
                 }
                 break;
             default:
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "' . $BlockName . '" at offset ' . $thisfile_ofr_thisblock['offset'];
                 fseek($fd, $BlockSize, SEEK_CUR);
                 break;
         }
     }
     if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) {
         $ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset'];
     }
     $ThisFileInfo['playtime_seconds'] = (double) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']);
     $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds'];
     // move the data chunk after all other chunks (if any)
     // so that the RIFF parser doesn't see EOF when trying
     // to skip over the data chunk
     $RIFFdata = substr($RIFFdata, 0, 36) . substr($RIFFdata, 44) . substr($RIFFdata, 36, 8);
     getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
     return true;
 }