2013-03-21 05:55:42 +01:00
< ? php
2019-09-14 21:07:57 +02:00
2013-03-21 05:55:42 +01:00
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
2019-09-14 21:07:57 +02:00
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
2013-03-21 05:55:42 +01:00
/////////////////////////////////////////////////////////////////
// //
// module.tag.apetag.php //
// module for analyzing APE tags //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
2020-07-02 17:48:04 +02:00
if ( ! defined ( 'GETID3_INCLUDEPATH' )) { // prevent path-exposing attacks that access modules directly on public webservers
exit ;
}
2013-03-21 05:55:42 +01:00
class getid3_apetag extends getid3_handler
{
2019-09-14 21:07:57 +02:00
/**
* true : return full data for all attachments ;
* false : return no data for all attachments ;
* integer : return data for attachments <= than this ;
* string : save as file to this directory .
*
* @ var int | bool | string
*/
public $inline_attachments = true ;
2013-03-21 05:55:42 +01:00
public $overrideendoffset = 0 ;
2019-09-14 21:07:57 +02:00
/**
* @ return bool
*/
2013-03-21 05:55:42 +01:00
public function Analyze () {
$info = & $this -> getid3 -> info ;
if ( ! getid3_lib :: intValueSupported ( $info [ 'filesize' ])) {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'Unable to check for APEtags because file is larger than ' . round ( PHP_INT_MAX / 1073741824 ) . 'GB' );
2013-03-21 05:55:42 +01:00
return false ;
}
$id3v1tagsize = 128 ;
$apetagheadersize = 32 ;
$lyrics3tagsize = 10 ;
if ( $this -> overrideendoffset == 0 ) {
2014-09-11 21:07:17 +02:00
$this -> fseek ( 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize , SEEK_END );
$APEfooterID3v1 = $this -> fread ( $id3v1tagsize + $apetagheadersize + $lyrics3tagsize );
2013-03-21 05:55:42 +01:00
//if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
if ( substr ( $APEfooterID3v1 , strlen ( $APEfooterID3v1 ) - $id3v1tagsize - $apetagheadersize , 8 ) == 'APETAGEX' ) {
// APE tag found before ID3v1
$info [ 'ape' ][ 'tag_offset_end' ] = $info [ 'filesize' ] - $id3v1tagsize ;
//} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
} elseif ( substr ( $APEfooterID3v1 , strlen ( $APEfooterID3v1 ) - $apetagheadersize , 8 ) == 'APETAGEX' ) {
// APE tag found, no ID3v1
$info [ 'ape' ][ 'tag_offset_end' ] = $info [ 'filesize' ];
}
} else {
2014-09-11 21:07:17 +02:00
$this -> fseek ( $this -> overrideendoffset - $apetagheadersize );
if ( $this -> fread ( 8 ) == 'APETAGEX' ) {
2013-03-21 05:55:42 +01:00
$info [ 'ape' ][ 'tag_offset_end' ] = $this -> overrideendoffset ;
}
}
if ( ! isset ( $info [ 'ape' ][ 'tag_offset_end' ])) {
// APE tag not found
unset ( $info [ 'ape' ]);
return false ;
}
// shortcut
$thisfile_ape = & $info [ 'ape' ];
2014-09-11 21:07:17 +02:00
$this -> fseek ( $thisfile_ape [ 'tag_offset_end' ] - $apetagheadersize );
$APEfooterData = $this -> fread ( 32 );
2013-03-21 05:55:42 +01:00
if ( ! ( $thisfile_ape [ 'footer' ] = $this -> parseAPEheaderFooter ( $APEfooterData ))) {
2017-07-31 21:50:45 +02:00
$this -> error ( 'Error parsing APE footer at offset ' . $thisfile_ape [ 'tag_offset_end' ]);
2013-03-21 05:55:42 +01:00
return false ;
}
if ( isset ( $thisfile_ape [ 'footer' ][ 'flags' ][ 'header' ]) && $thisfile_ape [ 'footer' ][ 'flags' ][ 'header' ]) {
2014-09-11 21:07:17 +02:00
$this -> fseek ( $thisfile_ape [ 'tag_offset_end' ] - $thisfile_ape [ 'footer' ][ 'raw' ][ 'tagsize' ] - $apetagheadersize );
$thisfile_ape [ 'tag_offset_start' ] = $this -> ftell ();
$APEtagData = $this -> fread ( $thisfile_ape [ 'footer' ][ 'raw' ][ 'tagsize' ] + $apetagheadersize );
2013-03-21 05:55:42 +01:00
} else {
$thisfile_ape [ 'tag_offset_start' ] = $thisfile_ape [ 'tag_offset_end' ] - $thisfile_ape [ 'footer' ][ 'raw' ][ 'tagsize' ];
2014-09-11 21:07:17 +02:00
$this -> fseek ( $thisfile_ape [ 'tag_offset_start' ]);
$APEtagData = $this -> fread ( $thisfile_ape [ 'footer' ][ 'raw' ][ 'tagsize' ]);
2013-03-21 05:55:42 +01:00
}
$info [ 'avdataend' ] = $thisfile_ape [ 'tag_offset_start' ];
if ( isset ( $info [ 'id3v1' ][ 'tag_offset_start' ]) && ( $info [ 'id3v1' ][ 'tag_offset_start' ] < $thisfile_ape [ 'tag_offset_end' ])) {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data' );
2013-03-21 05:55:42 +01:00
unset ( $info [ 'id3v1' ]);
foreach ( $info [ 'warning' ] as $key => $value ) {
if ( $value == 'Some ID3v1 fields do not use NULL characters for padding' ) {
unset ( $info [ 'warning' ][ $key ]);
sort ( $info [ 'warning' ]);
break ;
}
}
}
$offset = 0 ;
if ( isset ( $thisfile_ape [ 'footer' ][ 'flags' ][ 'header' ]) && $thisfile_ape [ 'footer' ][ 'flags' ][ 'header' ]) {
if ( $thisfile_ape [ 'header' ] = $this -> parseAPEheaderFooter ( substr ( $APEtagData , 0 , $apetagheadersize ))) {
$offset += $apetagheadersize ;
} else {
2017-07-31 21:50:45 +02:00
$this -> error ( 'Error parsing APE header at offset ' . $thisfile_ape [ 'tag_offset_start' ]);
2013-03-21 05:55:42 +01:00
return false ;
}
}
// shortcut
$info [ 'replay_gain' ] = array ();
$thisfile_replaygain = & $info [ 'replay_gain' ];
for ( $i = 0 ; $i < $thisfile_ape [ 'footer' ][ 'raw' ][ 'tag_items' ]; $i ++ ) {
$value_size = getid3_lib :: LittleEndian2Int ( substr ( $APEtagData , $offset , 4 ));
$offset += 4 ;
$item_flags = getid3_lib :: LittleEndian2Int ( substr ( $APEtagData , $offset , 4 ));
$offset += 4 ;
if ( strstr ( substr ( $APEtagData , $offset ), " \x00 " ) === false ) {
2017-07-31 21:50:45 +02:00
$this -> error ( 'Cannot find null-byte (0x00) separator between ItemKey #' . $i . ' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset ' . ( $thisfile_ape [ 'tag_offset_start' ] + $offset ));
2013-03-21 05:55:42 +01:00
return false ;
}
$ItemKeyLength = strpos ( $APEtagData , " \x00 " , $offset ) - $offset ;
$item_key = strtolower ( substr ( $APEtagData , $offset , $ItemKeyLength ));
// shortcut
$thisfile_ape [ 'items' ][ $item_key ] = array ();
$thisfile_ape_items_current = & $thisfile_ape [ 'items' ][ $item_key ];
$thisfile_ape_items_current [ 'offset' ] = $thisfile_ape [ 'tag_offset_start' ] + $offset ;
$offset += ( $ItemKeyLength + 1 ); // skip 0x00 terminator
$thisfile_ape_items_current [ 'data' ] = substr ( $APEtagData , $offset , $value_size );
$offset += $value_size ;
$thisfile_ape_items_current [ 'flags' ] = $this -> parseAPEtagFlags ( $item_flags );
switch ( $thisfile_ape_items_current [ 'flags' ][ 'item_contents_raw' ]) {
case 0 : // UTF-8
2015-06-28 02:17:25 +02:00
case 2 : // Locator (URL, filename, etc), UTF-8 encoded
$thisfile_ape_items_current [ 'data' ] = explode ( " \x00 " , $thisfile_ape_items_current [ 'data' ]);
2013-03-21 05:55:42 +01:00
break ;
2015-06-28 02:17:25 +02:00
case 1 : // binary data
default :
2013-03-21 05:55:42 +01:00
break ;
}
switch ( strtolower ( $item_key )) {
2015-06-28 02:17:25 +02:00
// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
2013-03-21 05:55:42 +01:00
case 'replaygain_track_gain' :
2019-09-14 21:07:57 +02:00
if ( preg_match ( '#^([\\-\\+][0-9\\.,]{8})( dB)?$#' , $thisfile_ape_items_current [ 'data' ][ 0 ], $matches )) {
$thisfile_replaygain [ 'track' ][ 'adjustment' ] = ( float ) str_replace ( ',' , '.' , $matches [ 1 ]); // float casting will see "0,95" as zero!
2015-06-28 02:17:25 +02:00
$thisfile_replaygain [ 'track' ][ 'originator' ] = 'unspecified' ;
} else {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'MP3gainTrackGain value in APEtag appears invalid: "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '"' );
2015-06-28 02:17:25 +02:00
}
2013-03-21 05:55:42 +01:00
break ;
case 'replaygain_track_peak' :
2019-09-14 21:07:57 +02:00
if ( preg_match ( '#^([0-9\\.,]{8})$#' , $thisfile_ape_items_current [ 'data' ][ 0 ], $matches )) {
$thisfile_replaygain [ 'track' ][ 'peak' ] = ( float ) str_replace ( ',' , '.' , $matches [ 1 ]); // float casting will see "0,95" as zero!
2015-06-28 02:17:25 +02:00
$thisfile_replaygain [ 'track' ][ 'originator' ] = 'unspecified' ;
if ( $thisfile_replaygain [ 'track' ][ 'peak' ] <= 0 ) {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'ReplayGain Track peak from APEtag appears invalid: ' . $thisfile_replaygain [ 'track' ][ 'peak' ] . ' (original value = "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '")' );
2015-06-28 02:17:25 +02:00
}
} else {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'MP3gainTrackPeak value in APEtag appears invalid: "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '"' );
2013-03-21 05:55:42 +01:00
}
break ;
case 'replaygain_album_gain' :
2019-09-14 21:07:57 +02:00
if ( preg_match ( '#^([\\-\\+][0-9\\.,]{8})( dB)?$#' , $thisfile_ape_items_current [ 'data' ][ 0 ], $matches )) {
$thisfile_replaygain [ 'album' ][ 'adjustment' ] = ( float ) str_replace ( ',' , '.' , $matches [ 1 ]); // float casting will see "0,95" as zero!
2015-06-28 02:17:25 +02:00
$thisfile_replaygain [ 'album' ][ 'originator' ] = 'unspecified' ;
} else {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'MP3gainAlbumGain value in APEtag appears invalid: "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '"' );
2015-06-28 02:17:25 +02:00
}
2013-03-21 05:55:42 +01:00
break ;
case 'replaygain_album_peak' :
2019-09-14 21:07:57 +02:00
if ( preg_match ( '#^([0-9\\.,]{8})$#' , $thisfile_ape_items_current [ 'data' ][ 0 ], $matches )) {
$thisfile_replaygain [ 'album' ][ 'peak' ] = ( float ) str_replace ( ',' , '.' , $matches [ 1 ]); // float casting will see "0,95" as zero!
2015-06-28 02:17:25 +02:00
$thisfile_replaygain [ 'album' ][ 'originator' ] = 'unspecified' ;
if ( $thisfile_replaygain [ 'album' ][ 'peak' ] <= 0 ) {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'ReplayGain Album peak from APEtag appears invalid: ' . $thisfile_replaygain [ 'album' ][ 'peak' ] . ' (original value = "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '")' );
2015-06-28 02:17:25 +02:00
}
} else {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'MP3gainAlbumPeak value in APEtag appears invalid: "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '"' );
2013-03-21 05:55:42 +01:00
}
break ;
case 'mp3gain_undo' :
2015-06-28 02:17:25 +02:00
if ( preg_match ( '#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#' , $thisfile_ape_items_current [ 'data' ][ 0 ])) {
list ( $mp3gain_undo_left , $mp3gain_undo_right , $mp3gain_undo_wrap ) = explode ( ',' , $thisfile_ape_items_current [ 'data' ][ 0 ]);
$thisfile_replaygain [ 'mp3gain' ][ 'undo_left' ] = intval ( $mp3gain_undo_left );
$thisfile_replaygain [ 'mp3gain' ][ 'undo_right' ] = intval ( $mp3gain_undo_right );
$thisfile_replaygain [ 'mp3gain' ][ 'undo_wrap' ] = (( $mp3gain_undo_wrap == 'Y' ) ? true : false );
} else {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'MP3gainUndo value in APEtag appears invalid: "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '"' );
2015-06-28 02:17:25 +02:00
}
2013-03-21 05:55:42 +01:00
break ;
case 'mp3gain_minmax' :
2015-06-28 02:17:25 +02:00
if ( preg_match ( '#^[0-9]{3},[0-9]{3}$#' , $thisfile_ape_items_current [ 'data' ][ 0 ])) {
list ( $mp3gain_globalgain_min , $mp3gain_globalgain_max ) = explode ( ',' , $thisfile_ape_items_current [ 'data' ][ 0 ]);
$thisfile_replaygain [ 'mp3gain' ][ 'globalgain_track_min' ] = intval ( $mp3gain_globalgain_min );
$thisfile_replaygain [ 'mp3gain' ][ 'globalgain_track_max' ] = intval ( $mp3gain_globalgain_max );
} else {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'MP3gainMinMax value in APEtag appears invalid: "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '"' );
2015-06-28 02:17:25 +02:00
}
2013-03-21 05:55:42 +01:00
break ;
case 'mp3gain_album_minmax' :
2015-06-28 02:17:25 +02:00
if ( preg_match ( '#^[0-9]{3},[0-9]{3}$#' , $thisfile_ape_items_current [ 'data' ][ 0 ])) {
list ( $mp3gain_globalgain_album_min , $mp3gain_globalgain_album_max ) = explode ( ',' , $thisfile_ape_items_current [ 'data' ][ 0 ]);
$thisfile_replaygain [ 'mp3gain' ][ 'globalgain_album_min' ] = intval ( $mp3gain_globalgain_album_min );
$thisfile_replaygain [ 'mp3gain' ][ 'globalgain_album_max' ] = intval ( $mp3gain_globalgain_album_max );
} else {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'MP3gainAlbumMinMax value in APEtag appears invalid: "' . $thisfile_ape_items_current [ 'data' ][ 0 ] . '"' );
2015-06-28 02:17:25 +02:00
}
2013-03-21 05:55:42 +01:00
break ;
case 'tracknumber' :
if ( is_array ( $thisfile_ape_items_current [ 'data' ])) {
foreach ( $thisfile_ape_items_current [ 'data' ] as $comment ) {
2019-09-14 21:07:57 +02:00
$thisfile_ape [ 'comments' ][ 'track_number' ][] = $comment ;
2013-03-21 05:55:42 +01:00
}
}
break ;
case 'cover art (artist)' :
case 'cover art (back)' :
case 'cover art (band logo)' :
case 'cover art (band)' :
case 'cover art (colored fish)' :
case 'cover art (composer)' :
case 'cover art (conductor)' :
case 'cover art (front)' :
case 'cover art (icon)' :
case 'cover art (illustration)' :
case 'cover art (lead)' :
case 'cover art (leaflet)' :
case 'cover art (lyricist)' :
case 'cover art (media)' :
case 'cover art (movie scene)' :
case 'cover art (other icon)' :
case 'cover art (other)' :
case 'cover art (performance)' :
case 'cover art (publisher logo)' :
case 'cover art (recording)' :
case 'cover art (studio)' :
2023-10-20 15:29:27 +02:00
// list of possible cover arts from https://github.com/mono/taglib-sharp/blob/taglib-sharp-2.0.3.2/src/TagLib/Ape/Tag.cs
2015-06-28 02:17:25 +02:00
if ( is_array ( $thisfile_ape_items_current [ 'data' ])) {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'APEtag "' . $item_key . '" should be flagged as Binary data, but was incorrectly flagged as UTF-8' );
2015-06-28 02:17:25 +02:00
$thisfile_ape_items_current [ 'data' ] = implode ( " \x00 " , $thisfile_ape_items_current [ 'data' ]);
}
2013-03-21 05:55:42 +01:00
list ( $thisfile_ape_items_current [ 'filename' ], $thisfile_ape_items_current [ 'data' ]) = explode ( " \x00 " , $thisfile_ape_items_current [ 'data' ], 2 );
$thisfile_ape_items_current [ 'data_offset' ] = $thisfile_ape_items_current [ 'offset' ] + strlen ( $thisfile_ape_items_current [ 'filename' ] . " \x00 " );
$thisfile_ape_items_current [ 'data_length' ] = strlen ( $thisfile_ape_items_current [ 'data' ]);
do {
2017-07-31 21:50:45 +02:00
$thisfile_ape_items_current [ 'image_mime' ] = '' ;
$imageinfo = array ();
$imagechunkcheck = getid3_lib :: GetDataImageSize ( $thisfile_ape_items_current [ 'data' ], $imageinfo );
if (( $imagechunkcheck === false ) || ! isset ( $imagechunkcheck [ 2 ])) {
$this -> warning ( 'APEtag "' . $item_key . '" contains invalid image data' );
break ;
}
$thisfile_ape_items_current [ 'image_mime' ] = image_type_to_mime_type ( $imagechunkcheck [ 2 ]);
2013-03-21 05:55:42 +01:00
if ( $this -> inline_attachments === false ) {
// skip entirely
unset ( $thisfile_ape_items_current [ 'data' ]);
break ;
}
if ( $this -> inline_attachments === true ) {
// great
} elseif ( is_int ( $this -> inline_attachments )) {
if ( $this -> inline_attachments < $thisfile_ape_items_current [ 'data_length' ]) {
// too big, skip
2017-07-31 21:50:45 +02:00
$this -> warning ( 'attachment at ' . $thisfile_ape_items_current [ 'offset' ] . ' is too large to process inline (' . number_format ( $thisfile_ape_items_current [ 'data_length' ]) . ' bytes)' );
2013-03-21 05:55:42 +01:00
unset ( $thisfile_ape_items_current [ 'data' ]);
break ;
}
} elseif ( is_string ( $this -> inline_attachments )) {
$this -> inline_attachments = rtrim ( str_replace ( array ( '/' , '\\' ), DIRECTORY_SEPARATOR , $this -> inline_attachments ), DIRECTORY_SEPARATOR );
2017-07-31 21:50:45 +02:00
if ( ! is_dir ( $this -> inline_attachments ) || ! getID3 :: is_writable ( $this -> inline_attachments )) {
2013-03-21 05:55:42 +01:00
// cannot write, skip
2017-07-31 21:50:45 +02:00
$this -> warning ( 'attachment at ' . $thisfile_ape_items_current [ 'offset' ] . ' cannot be saved to "' . $this -> inline_attachments . '" (not writable)' );
2013-03-21 05:55:42 +01:00
unset ( $thisfile_ape_items_current [ 'data' ]);
break ;
}
}
// if we get this far, must be OK
if ( is_string ( $this -> inline_attachments )) {
$destination_filename = $this -> inline_attachments . DIRECTORY_SEPARATOR . md5 ( $info [ 'filenamepath' ]) . '_' . $thisfile_ape_items_current [ 'data_offset' ];
2017-07-31 21:50:45 +02:00
if ( ! file_exists ( $destination_filename ) || getID3 :: is_writable ( $destination_filename )) {
2013-03-21 05:55:42 +01:00
file_put_contents ( $destination_filename , $thisfile_ape_items_current [ 'data' ]);
} else {
2017-07-31 21:50:45 +02:00
$this -> warning ( 'attachment at ' . $thisfile_ape_items_current [ 'offset' ] . ' cannot be saved to "' . $destination_filename . '" (not writable)' );
2013-03-21 05:55:42 +01:00
}
$thisfile_ape_items_current [ 'data_filename' ] = $destination_filename ;
unset ( $thisfile_ape_items_current [ 'data' ]);
} else {
if ( ! isset ( $info [ 'ape' ][ 'comments' ][ 'picture' ])) {
$info [ 'ape' ][ 'comments' ][ 'picture' ] = array ();
}
2015-06-28 02:17:25 +02:00
$comments_picture_data = array ();
foreach ( array ( 'data' , 'image_mime' , 'image_width' , 'image_height' , 'imagetype' , 'picturetype' , 'description' , 'datalength' ) as $picture_key ) {
if ( isset ( $thisfile_ape_items_current [ $picture_key ])) {
$comments_picture_data [ $picture_key ] = $thisfile_ape_items_current [ $picture_key ];
}
}
$info [ 'ape' ][ 'comments' ][ 'picture' ][] = $comments_picture_data ;
unset ( $comments_picture_data );
2013-03-21 05:55:42 +01:00
}
2023-10-20 15:29:27 +02:00
} while ( false ); // @phpstan-ignore-line
2013-03-21 05:55:42 +01:00
break ;
default :
if ( is_array ( $thisfile_ape_items_current [ 'data' ])) {
foreach ( $thisfile_ape_items_current [ 'data' ] as $comment ) {
$thisfile_ape [ 'comments' ][ strtolower ( $item_key )][] = $comment ;
}
}
break ;
}
}
if ( empty ( $thisfile_replaygain )) {
unset ( $info [ 'replay_gain' ]);
}
return true ;
}
2019-09-14 21:07:57 +02:00
/**
* @ param string $APEheaderFooterData
*
* @ return array | false
*/
2013-03-21 05:55:42 +01:00
public function parseAPEheaderFooter ( $APEheaderFooterData ) {
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
// shortcut
2021-11-26 04:06:03 +01:00
$headerfooterinfo = array ();
2013-03-21 05:55:42 +01:00
$headerfooterinfo [ 'raw' ] = array ();
$headerfooterinfo_raw = & $headerfooterinfo [ 'raw' ];
$headerfooterinfo_raw [ 'footer_tag' ] = substr ( $APEheaderFooterData , 0 , 8 );
if ( $headerfooterinfo_raw [ 'footer_tag' ] != 'APETAGEX' ) {
return false ;
}
$headerfooterinfo_raw [ 'version' ] = getid3_lib :: LittleEndian2Int ( substr ( $APEheaderFooterData , 8 , 4 ));
$headerfooterinfo_raw [ 'tagsize' ] = getid3_lib :: LittleEndian2Int ( substr ( $APEheaderFooterData , 12 , 4 ));
$headerfooterinfo_raw [ 'tag_items' ] = getid3_lib :: LittleEndian2Int ( substr ( $APEheaderFooterData , 16 , 4 ));
$headerfooterinfo_raw [ 'global_flags' ] = getid3_lib :: LittleEndian2Int ( substr ( $APEheaderFooterData , 20 , 4 ));
$headerfooterinfo_raw [ 'reserved' ] = substr ( $APEheaderFooterData , 24 , 8 );
$headerfooterinfo [ 'tag_version' ] = $headerfooterinfo_raw [ 'version' ] / 1000 ;
if ( $headerfooterinfo [ 'tag_version' ] >= 2 ) {
$headerfooterinfo [ 'flags' ] = $this -> parseAPEtagFlags ( $headerfooterinfo_raw [ 'global_flags' ]);
}
return $headerfooterinfo ;
}
2019-09-14 21:07:57 +02:00
/**
* @ param int $rawflagint
*
* @ return array
*/
2013-03-21 05:55:42 +01:00
public function parseAPEtagFlags ( $rawflagint ) {
// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
// All are set to zero on creation and ignored on reading."
2015-06-28 02:17:25 +02:00
// http://wiki.hydrogenaud.io/index.php?title=Ape_Tags_Flags
2021-11-26 04:06:03 +01:00
$flags = array ();
2013-03-21 05:55:42 +01:00
$flags [ 'header' ] = ( bool ) ( $rawflagint & 0x80000000 );
$flags [ 'footer' ] = ( bool ) ( $rawflagint & 0x40000000 );
$flags [ 'this_is_header' ] = ( bool ) ( $rawflagint & 0x20000000 );
$flags [ 'item_contents_raw' ] = ( $rawflagint & 0x00000006 ) >> 1 ;
$flags [ 'read_only' ] = ( bool ) ( $rawflagint & 0x00000001 );
$flags [ 'item_contents' ] = $this -> APEcontentTypeFlagLookup ( $flags [ 'item_contents_raw' ]);
return $flags ;
}
2019-09-14 21:07:57 +02:00
/**
* @ param int $contenttypeid
*
* @ return string
*/
2013-03-21 05:55:42 +01:00
public function APEcontentTypeFlagLookup ( $contenttypeid ) {
static $APEcontentTypeFlagLookup = array (
0 => 'utf-8' ,
1 => 'binary' ,
2 => 'external' ,
3 => 'reserved'
);
return ( isset ( $APEcontentTypeFlagLookup [ $contenttypeid ]) ? $APEcontentTypeFlagLookup [ $contenttypeid ] : 'invalid' );
}
2019-09-14 21:07:57 +02:00
/**
* @ param string $itemkey
*
* @ return bool
*/
2013-03-21 05:55:42 +01:00
public function APEtagItemIsUTF8Lookup ( $itemkey ) {
static $APEtagItemIsUTF8Lookup = array (
'title' ,
'subtitle' ,
'artist' ,
'album' ,
'debut album' ,
'publisher' ,
'conductor' ,
'track' ,
'composer' ,
'comment' ,
'copyright' ,
'publicationright' ,
'file' ,
'year' ,
'record date' ,
'record location' ,
'genre' ,
'media' ,
'related' ,
'isrc' ,
'abstract' ,
'language' ,
'bibliography'
);
return in_array ( strtolower ( $itemkey ), $APEtagItemIsUTF8Lookup );
}
2015-06-28 02:17:25 +02:00
}