module.tag.id3v2.php 147 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741
  1. <?php
  2. /////////////////////////////////////////////////////////////////
  3. /// getID3() by James Heinrich <info@getid3.org> //
  4. // available at http://getid3.sourceforge.net //
  5. // or http://www.getid3.org //
  6. // also https://github.com/JamesHeinrich/getID3 //
  7. /////////////////////////////////////////////////////////////////
  8. // See readme.txt for more details //
  9. /////////////////////////////////////////////////////////////////
  10. /// //
  11. // module.tag.id3v2.php //
  12. // module for analyzing ID3v2 tags //
  13. // dependencies: module.tag.id3v1.php //
  14. // ///
  15. /////////////////////////////////////////////////////////////////
  16. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
  17. class getid3_id3v2 extends getid3_handler
  18. {
  19. public $StartingOffset = 0;
  20. public function Analyze() {
  21. $info = &$this->getid3->info;
  22. // Overall tag structure:
  23. // +-----------------------------+
  24. // | Header (10 bytes) |
  25. // +-----------------------------+
  26. // | Extended Header |
  27. // | (variable length, OPTIONAL) |
  28. // +-----------------------------+
  29. // | Frames (variable length) |
  30. // +-----------------------------+
  31. // | Padding |
  32. // | (variable length, OPTIONAL) |
  33. // +-----------------------------+
  34. // | Footer (10 bytes, OPTIONAL) |
  35. // +-----------------------------+
  36. // Header
  37. // ID3v2/file identifier "ID3"
  38. // ID3v2 version $04 00
  39. // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x)
  40. // ID3v2 size 4 * %0xxxxxxx
  41. // shortcuts
  42. $info['id3v2']['header'] = true;
  43. $thisfile_id3v2 = &$info['id3v2'];
  44. $thisfile_id3v2['flags'] = array();
  45. $thisfile_id3v2_flags = &$thisfile_id3v2['flags'];
  46. $this->fseek($this->StartingOffset);
  47. $header = $this->fread(10);
  48. if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
  49. $thisfile_id3v2['majorversion'] = ord($header{3});
  50. $thisfile_id3v2['minorversion'] = ord($header{4});
  51. // shortcut
  52. $id3v2_majorversion = &$thisfile_id3v2['majorversion'];
  53. } else {
  54. unset($info['id3v2']);
  55. return false;
  56. }
  57. if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
  58. $this->error('this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']);
  59. return false;
  60. }
  61. $id3_flags = ord($header{5});
  62. switch ($id3v2_majorversion) {
  63. case 2:
  64. // %ab000000 in v2.2
  65. $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
  66. $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression
  67. break;
  68. case 3:
  69. // %abc00000 in v2.3
  70. $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
  71. $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
  72. $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
  73. break;
  74. case 4:
  75. // %abcd0000 in v2.4
  76. $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
  77. $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
  78. $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
  79. $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present
  80. break;
  81. }
  82. $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
  83. $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset;
  84. $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
  85. // create 'encoding' key - used by getid3::HandleAllTags()
  86. // in ID3v2 every field can have it's own encoding type
  87. // so force everything to UTF-8 so it can be handled consistantly
  88. $thisfile_id3v2['encoding'] = 'UTF-8';
  89. // Frames
  90. // All ID3v2 frames consists of one frame header followed by one or more
  91. // fields containing the actual information. The header is always 10
  92. // bytes and laid out as follows:
  93. //
  94. // Frame ID $xx xx xx xx (four characters)
  95. // Size 4 * %0xxxxxxx
  96. // Flags $xx xx
  97. $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
  98. if (!empty($thisfile_id3v2['exthead']['length'])) {
  99. $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4);
  100. }
  101. if (!empty($thisfile_id3v2_flags['isfooter'])) {
  102. $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
  103. }
  104. if ($sizeofframes > 0) {
  105. $framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable
  106. // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
  107. if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) {
  108. $framedata = $this->DeUnsynchronise($framedata);
  109. }
  110. // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
  111. // of on tag level, making it easier to skip frames, increasing the streamability
  112. // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
  113. // there exists an unsynchronised frame, while the new unsynchronisation flag in
  114. // the frame header [S:4.1.2] indicates unsynchronisation.
  115. //$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
  116. $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
  117. // Extended Header
  118. if (!empty($thisfile_id3v2_flags['exthead'])) {
  119. $extended_header_offset = 0;
  120. if ($id3v2_majorversion == 3) {
  121. // v2.3 definition:
  122. //Extended header size $xx xx xx xx // 32-bit integer
  123. //Extended Flags $xx xx
  124. // %x0000000 %00000000 // v2.3
  125. // x - CRC data present
  126. //Size of padding $xx xx xx xx
  127. $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0);
  128. $extended_header_offset += 4;
  129. $thisfile_id3v2['exthead']['flag_bytes'] = 2;
  130. $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
  131. $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
  132. $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000);
  133. $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
  134. $extended_header_offset += 4;
  135. if ($thisfile_id3v2['exthead']['flags']['crc']) {
  136. $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
  137. $extended_header_offset += 4;
  138. }
  139. $extended_header_offset += $thisfile_id3v2['exthead']['padding_size'];
  140. } elseif ($id3v2_majorversion == 4) {
  141. // v2.4 definition:
  142. //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer
  143. //Number of flag bytes $01
  144. //Extended Flags $xx
  145. // %0bcd0000 // v2.4
  146. // b - Tag is an update
  147. // Flag data length $00
  148. // c - CRC data present
  149. // Flag data length $05
  150. // Total frame CRC 5 * %0xxxxxxx
  151. // d - Tag restrictions
  152. // Flag data length $01
  153. $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true);
  154. $extended_header_offset += 4;
  155. $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1
  156. $extended_header_offset += 1;
  157. $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
  158. $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
  159. $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40);
  160. $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20);
  161. $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10);
  162. if ($thisfile_id3v2['exthead']['flags']['update']) {
  163. $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0
  164. $extended_header_offset += 1;
  165. }
  166. if ($thisfile_id3v2['exthead']['flags']['crc']) {
  167. $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5
  168. $extended_header_offset += 1;
  169. $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false);
  170. $extended_header_offset += $ext_header_chunk_length;
  171. }
  172. if ($thisfile_id3v2['exthead']['flags']['restrictions']) {
  173. $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1
  174. $extended_header_offset += 1;
  175. // %ppqrrstt
  176. $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1));
  177. $extended_header_offset += 1;
  178. $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions
  179. $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions
  180. $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions
  181. $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions
  182. $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions
  183. $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']);
  184. $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']);
  185. $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']);
  186. $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']);
  187. $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']);
  188. }
  189. if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) {
  190. $this->warning('ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')');
  191. }
  192. }
  193. $framedataoffset += $extended_header_offset;
  194. $framedata = substr($framedata, $extended_header_offset);
  195. } // end extended header
  196. while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
  197. if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
  198. // insufficient room left in ID3v2 header for actual data - must be padding
  199. $thisfile_id3v2['padding']['start'] = $framedataoffset;
  200. $thisfile_id3v2['padding']['length'] = strlen($framedata);
  201. $thisfile_id3v2['padding']['valid'] = true;
  202. for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
  203. if ($framedata{$i} != "\x00") {
  204. $thisfile_id3v2['padding']['valid'] = false;
  205. $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
  206. $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
  207. break;
  208. }
  209. }
  210. break; // skip rest of ID3v2 header
  211. }
  212. if ($id3v2_majorversion == 2) {
  213. // Frame ID $xx xx xx (three characters)
  214. // Size $xx xx xx (24-bit integer)
  215. // Flags $xx xx
  216. $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header
  217. $framedata = substr($framedata, 6); // and leave the rest in $framedata
  218. $frame_name = substr($frame_header, 0, 3);
  219. $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0);
  220. $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs
  221. } elseif ($id3v2_majorversion > 2) {
  222. // Frame ID $xx xx xx xx (four characters)
  223. // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+)
  224. // Flags $xx xx
  225. $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header
  226. $framedata = substr($framedata, 10); // and leave the rest in $framedata
  227. $frame_name = substr($frame_header, 0, 4);
  228. if ($id3v2_majorversion == 3) {
  229. $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
  230. } else { // ID3v2.4+
  231. $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value)
  232. }
  233. if ($frame_size < (strlen($framedata) + 4)) {
  234. $nextFrameID = substr($framedata, $frame_size, 4);
  235. if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) {
  236. // next frame is OK
  237. } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
  238. // MP3ext known broken frames - "ok" for the purposes of this test
  239. } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
  240. $this->warning('ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3');
  241. $id3v2_majorversion = 3;
  242. $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
  243. }
  244. }
  245. $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2));
  246. }
  247. if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) {
  248. // padding encountered
  249. $thisfile_id3v2['padding']['start'] = $framedataoffset;
  250. $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata);
  251. $thisfile_id3v2['padding']['valid'] = true;
  252. $len = strlen($framedata);
  253. for ($i = 0; $i < $len; $i++) {
  254. if ($framedata{$i} != "\x00") {
  255. $thisfile_id3v2['padding']['valid'] = false;
  256. $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
  257. $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
  258. break;
  259. }
  260. }
  261. break; // skip rest of ID3v2 header
  262. }
  263. if ($iTunesBrokenFrameNameFixed = self::ID3v22iTunesBrokenFrameName($frame_name)) {
  264. $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1", "v7.0.0.70" are known-guilty, probably others too)]. Translated frame name from "'.str_replace("\x00", ' ', $frame_name).'" to "'.$iTunesBrokenFrameNameFixed.'" for parsing.');
  265. $frame_name = $iTunesBrokenFrameNameFixed;
  266. }
  267. if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
  268. unset($parsedFrame);
  269. $parsedFrame['frame_name'] = $frame_name;
  270. $parsedFrame['frame_flags_raw'] = $frame_flags;
  271. $parsedFrame['data'] = substr($framedata, 0, $frame_size);
  272. $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size);
  273. $parsedFrame['dataoffset'] = $framedataoffset;
  274. $this->ParseID3v2Frame($parsedFrame);
  275. $thisfile_id3v2[$frame_name][] = $parsedFrame;
  276. $framedata = substr($framedata, $frame_size);
  277. } else { // invalid frame length or FrameID
  278. if ($frame_size <= strlen($framedata)) {
  279. if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) {
  280. // next frame is valid, just skip the current frame
  281. $framedata = substr($framedata, $frame_size);
  282. $this->warning('Next ID3v2 frame is valid, skipping current frame.');
  283. } else {
  284. // next frame is invalid too, abort processing
  285. //unset($framedata);
  286. $framedata = null;
  287. $this->error('Next ID3v2 frame is also invalid, aborting processing.');
  288. }
  289. } elseif ($frame_size == strlen($framedata)) {
  290. // this is the last frame, just skip
  291. $this->warning('This was the last ID3v2 frame.');
  292. } else {
  293. // next frame is invalid too, abort processing
  294. //unset($framedata);
  295. $framedata = null;
  296. $this->warning('Invalid ID3v2 frame size, aborting.');
  297. }
  298. if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) {
  299. switch ($frame_name) {
  300. case "\x00\x00".'MP':
  301. case "\x00".'MP3':
  302. case ' MP3':
  303. case 'MP3e':
  304. case "\x00".'MP':
  305. case ' MP':
  306. case 'MP3':
  307. $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]');
  308. break;
  309. default:
  310. $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).');
  311. break;
  312. }
  313. } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) {
  314. $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).');
  315. } else {
  316. $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).');
  317. }
  318. }
  319. $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion));
  320. }
  321. }
  322. // Footer
  323. // The footer is a copy of the header, but with a different identifier.
  324. // ID3v2 identifier "3DI"
  325. // ID3v2 version $04 00
  326. // ID3v2 flags %abcd0000
  327. // ID3v2 size 4 * %0xxxxxxx
  328. if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
  329. $footer = $this->fread(10);
  330. if (substr($footer, 0, 3) == '3DI') {
  331. $thisfile_id3v2['footer'] = true;
  332. $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
  333. $thisfile_id3v2['minorversion_footer'] = ord($footer{4});
  334. }
  335. if ($thisfile_id3v2['majorversion_footer'] <= 4) {
  336. $id3_flags = ord(substr($footer{5}));
  337. $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80);
  338. $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40);
  339. $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20);
  340. $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10);
  341. $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1);
  342. }
  343. } // end footer
  344. if (isset($thisfile_id3v2['comments']['genre'])) {
  345. $genres = array();
  346. foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
  347. foreach ($this->ParseID3v2GenreString($value) as $genre) {
  348. $genres[] = $genre;
  349. }
  350. }
  351. $thisfile_id3v2['comments']['genre'] = array_unique($genres);
  352. unset($key, $value, $genres, $genre);
  353. }
  354. if (isset($thisfile_id3v2['comments']['track'])) {
  355. foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
  356. if (strstr($value, '/')) {
  357. list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
  358. }
  359. }
  360. }
  361. if (!isset($thisfile_id3v2['comments']['year']) && !empty($thisfile_id3v2['comments']['recording_time'][0]) && preg_match('#^([0-9]{4})#', trim($thisfile_id3v2['comments']['recording_time'][0]), $matches)) {
  362. $thisfile_id3v2['comments']['year'] = array($matches[1]);
  363. }
  364. if (!empty($thisfile_id3v2['TXXX'])) {
  365. // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames
  366. foreach ($thisfile_id3v2['TXXX'] as $txxx_array) {
  367. switch ($txxx_array['description']) {
  368. case 'replaygain_track_gain':
  369. if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) {
  370. $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
  371. }
  372. break;
  373. case 'replaygain_track_peak':
  374. if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) {
  375. $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']);
  376. }
  377. break;
  378. case 'replaygain_album_gain':
  379. if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) {
  380. $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
  381. }
  382. break;
  383. }
  384. }
  385. }
  386. // Set avdataoffset
  387. $info['avdataoffset'] = $thisfile_id3v2['headerlength'];
  388. if (isset($thisfile_id3v2['footer'])) {
  389. $info['avdataoffset'] += 10;
  390. }
  391. return true;
  392. }
  393. public function ParseID3v2GenreString($genrestring) {
  394. // Parse genres into arrays of genreName and genreID
  395. // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
  396. // ID3v2.4.x: '21' $00 'Eurodisco' $00
  397. $clean_genres = array();
  398. // hack-fixes for some badly-written ID3v2.3 taggers, while trying not to break correctly-written tags
  399. if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) {
  400. // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here:
  401. // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name
  402. if (preg_match('#/#', $genrestring)) {
  403. $genrestring = str_replace('/', "\x00", $genrestring);
  404. $genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring);
  405. $genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring);
  406. }
  407. // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
  408. if (preg_match('#;#', $genrestring)) {
  409. $genrestring = str_replace(';', "\x00", $genrestring);
  410. }
  411. }
  412. if (strpos($genrestring, "\x00") === false) {
  413. $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
  414. }
  415. $genre_elements = explode("\x00", $genrestring);
  416. foreach ($genre_elements as $element) {
  417. $element = trim($element);
  418. if ($element) {
  419. if (preg_match('#^[0-9]{1,3}#', $element)) {
  420. $clean_genres[] = getid3_id3v1::LookupGenreName($element);
  421. } else {
  422. $clean_genres[] = str_replace('((', '(', $element);
  423. }
  424. }
  425. }
  426. return $clean_genres;
  427. }
  428. public function ParseID3v2Frame(&$parsedFrame) {
  429. // shortcuts
  430. $info = &$this->getid3->info;
  431. $id3v2_majorversion = $info['id3v2']['majorversion'];
  432. $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
  433. if (empty($parsedFrame['framenamelong'])) {
  434. unset($parsedFrame['framenamelong']);
  435. }
  436. $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
  437. if (empty($parsedFrame['framenameshort'])) {
  438. unset($parsedFrame['framenameshort']);
  439. }
  440. if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard
  441. if ($id3v2_majorversion == 3) {
  442. // Frame Header Flags
  443. // %abc00000 %ijk00000
  444. $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation
  445. $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation
  446. $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only
  447. $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression
  448. $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption
  449. $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity
  450. } elseif ($id3v2_majorversion == 4) {
  451. // Frame Header Flags
  452. // %0abc0000 %0h00kmnp
  453. $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation
  454. $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation
  455. $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only
  456. $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity
  457. $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression
  458. $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption
  459. $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation
  460. $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator
  461. // Frame-level de-unsynchronisation - ID3v2.4
  462. if ($parsedFrame['flags']['Unsynchronisation']) {
  463. $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
  464. }
  465. if ($parsedFrame['flags']['DataLengthIndicator']) {
  466. $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1);
  467. $parsedFrame['data'] = substr($parsedFrame['data'], 4);
  468. }
  469. }
  470. // Frame-level de-compression
  471. if ($parsedFrame['flags']['compression']) {
  472. $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
  473. if (!function_exists('gzuncompress')) {
  474. $this->warning('gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"');
  475. } else {
  476. if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
  477. //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) {
  478. $parsedFrame['data'] = $decompresseddata;
  479. unset($decompresseddata);
  480. } else {
  481. $this->warning('gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"');
  482. }
  483. }
  484. }
  485. }
  486. if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
  487. if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
  488. $this->warning('ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data');
  489. }
  490. }
  491. if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) {
  492. $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion';
  493. switch ($parsedFrame['frame_name']) {
  494. case 'WCOM':
  495. $warning .= ' (this is known to happen with files tagged by RioPort)';
  496. break;
  497. default:
  498. break;
  499. }
  500. $this->warning($warning);
  501. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier
  502. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier
  503. // There may be more than one 'UFID' frame in a tag,
  504. // but only one with the same 'Owner identifier'.
  505. // <Header for 'Unique file identifier', ID: 'UFID'>
  506. // Owner identifier <text string> $00
  507. // Identifier <up to 64 bytes binary data>
  508. $exploded = explode("\x00", $parsedFrame['data'], 2);
  509. $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : '');
  510. $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : '');
  511. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame
  512. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame
  513. // There may be more than one 'TXXX' frame in each tag,
  514. // but only one with the same description.
  515. // <Header for 'User defined text information frame', ID: 'TXXX'>
  516. // Text encoding $xx
  517. // Description <text string according to encoding> $00 (00)
  518. // Value <text string according to encoding>
  519. $frame_offset = 0;
  520. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  521. $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
  522. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  523. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  524. $frame_textencoding_terminator = "\x00";
  525. }
  526. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  527. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  528. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  529. }
  530. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  531. if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
  532. // if description only contains a BOM or terminator then make it blank
  533. $frame_description = '';
  534. }
  535. $parsedFrame['encodingid'] = $frame_textencoding;
  536. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  537. $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
  538. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
  539. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  540. $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
  541. if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
  542. $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
  543. } else {
  544. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
  545. }
  546. }
  547. //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
  548. } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
  549. // There may only be one text information frame of its kind in an tag.
  550. // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
  551. // excluding 'TXXX' described in 4.2.6.>
  552. // Text encoding $xx
  553. // Information <text string(s) according to encoding>
  554. $frame_offset = 0;
  555. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  556. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  557. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  558. }
  559. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  560. $parsedFrame['encodingid'] = $frame_textencoding;
  561. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  562. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  563. // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
  564. // This of course breaks when an artist name contains slash character, e.g. "AC/DC"
  565. // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense
  566. // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user
  567. switch ($parsedFrame['encoding']) {
  568. case 'UTF-16':
  569. case 'UTF-16BE':
  570. case 'UTF-16LE':
  571. $wordsize = 2;
  572. break;
  573. case 'ISO-8859-1':
  574. case 'UTF-8':
  575. default:
  576. $wordsize = 1;
  577. break;
  578. }
  579. $Txxx_elements = array();
  580. $Txxx_elements_start_offset = 0;
  581. for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) {
  582. if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) {
  583. $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
  584. $Txxx_elements_start_offset = $i + $wordsize;
  585. }
  586. }
  587. $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
  588. foreach ($Txxx_elements as $Txxx_element) {
  589. $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element);
  590. if (!empty($string)) {
  591. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
  592. }
  593. }
  594. unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset);
  595. }
  596. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame
  597. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame
  598. // There may be more than one 'WXXX' frame in each tag,
  599. // but only one with the same description
  600. // <Header for 'User defined URL link frame', ID: 'WXXX'>
  601. // Text encoding $xx
  602. // Description <text string according to encoding> $00 (00)
  603. // URL <text string>
  604. $frame_offset = 0;
  605. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  606. $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
  607. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  608. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  609. $frame_textencoding_terminator = "\x00";
  610. }
  611. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  612. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  613. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  614. }
  615. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  616. if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
  617. // if description only contains a BOM or terminator then make it blank
  618. $frame_description = '';
  619. }
  620. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
  621. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
  622. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  623. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  624. }
  625. if ($frame_terminatorpos) {
  626. // there are null bytes after the data - this is not according to spec
  627. // only use data up to first null byte
  628. $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
  629. } else {
  630. // no null bytes following data, just use all data
  631. $frame_urldata = (string) $parsedFrame['data'];
  632. }
  633. $parsedFrame['encodingid'] = $frame_textencoding;
  634. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  635. $parsedFrame['url'] = $frame_urldata;
  636. $parsedFrame['description'] = $frame_description;
  637. if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
  638. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
  639. }
  640. unset($parsedFrame['data']);
  641. } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
  642. // There may only be one URL link frame of its kind in a tag,
  643. // except when stated otherwise in the frame description
  644. // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
  645. // described in 4.3.2.>
  646. // URL <text string>
  647. $parsedFrame['url'] = trim($parsedFrame['data']);
  648. if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
  649. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
  650. }
  651. unset($parsedFrame['data']);
  652. } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only)
  653. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only)
  654. // http://id3.org/id3v2.3.0#sec4.4
  655. // There may only be one 'IPL' frame in each tag
  656. // <Header for 'User defined URL link frame', ID: 'IPL'>
  657. // Text encoding $xx
  658. // People list strings <textstrings>
  659. $frame_offset = 0;
  660. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  661. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  662. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  663. }
  664. $parsedFrame['encodingid'] = $frame_textencoding;
  665. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
  666. $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset);
  667. // http://www.getid3.org/phpBB3/viewtopic.php?t=1369
  668. // "this tag typically contains null terminated strings, which are associated in pairs"
  669. // "there are users that use the tag incorrectly"
  670. $IPLS_parts = array();
  671. if (strpos($parsedFrame['data_raw'], "\x00") !== false) {
  672. $IPLS_parts_unsorted = array();
  673. if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) {
  674. // UTF-16, be careful looking for null bytes since most 2-byte characters may contain one; you need to find twin null bytes, and on even padding
  675. $thisILPS = '';
  676. for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) {
  677. $twobytes = substr($parsedFrame['data_raw'], $i, 2);
  678. if ($twobytes === "\x00\x00") {
  679. $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
  680. $thisILPS = '';
  681. } else {
  682. $thisILPS .= $twobytes;
  683. }
  684. }
  685. if (strlen($thisILPS) > 2) { // 2-byte BOM
  686. $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
  687. }
  688. } else {
  689. // ISO-8859-1 or UTF-8 or other single-byte-null character set
  690. $IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']);
  691. }
  692. if (count($IPLS_parts_unsorted) == 1) {
  693. // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson"
  694. foreach ($IPLS_parts_unsorted as $key => $value) {
  695. $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value);
  696. $position = '';
  697. foreach ($IPLS_parts_sorted as $person) {
  698. $IPLS_parts[] = array('position'=>$position, 'person'=>$person);
  699. }
  700. }
  701. } elseif ((count($IPLS_parts_unsorted) % 2) == 0) {
  702. $position = '';
  703. $person = '';
  704. foreach ($IPLS_parts_unsorted as $key => $value) {
  705. if (($key % 2) == 0) {
  706. $position = $value;
  707. } else {
  708. $person = $value;
  709. $IPLS_parts[] = array('position'=>$position, 'person'=>$person);
  710. $position = '';
  711. $person = '';
  712. }
  713. }
  714. } else {
  715. foreach ($IPLS_parts_unsorted as $key => $value) {
  716. $IPLS_parts[] = array($value);
  717. }
  718. }
  719. } else {
  720. $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']);
  721. }
  722. $parsedFrame['data'] = $IPLS_parts;
  723. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  724. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
  725. }
  726. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier
  727. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier
  728. // There may only be one 'MCDI' frame in each tag
  729. // <Header for 'Music CD identifier', ID: 'MCDI'>
  730. // CD TOC <binary data>
  731. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  732. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
  733. }
  734. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes
  735. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes
  736. // There may only be one 'ETCO' frame in each tag
  737. // <Header for 'Event timing codes', ID: 'ETCO'>
  738. // Time stamp format $xx
  739. // Where time stamp format is:
  740. // $01 (32-bit value) MPEG frames from beginning of file
  741. // $02 (32-bit value) milliseconds from beginning of file
  742. // Followed by a list of key events in the following format:
  743. // Type of event $xx
  744. // Time stamp $xx (xx ...)
  745. // The 'Time stamp' is set to zero if directly at the beginning of the sound
  746. // or after the previous event. All events MUST be sorted in chronological order.
  747. $frame_offset = 0;
  748. $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  749. while ($frame_offset < strlen($parsedFrame['data'])) {
  750. $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
  751. $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
  752. $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  753. $frame_offset += 4;
  754. }
  755. unset($parsedFrame['data']);
  756. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table
  757. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table
  758. // There may only be one 'MLLT' frame in each tag
  759. // <Header for 'Location lookup table', ID: 'MLLT'>
  760. // MPEG frames between reference $xx xx
  761. // Bytes between reference $xx xx xx
  762. // Milliseconds between reference $xx xx xx
  763. // Bits for bytes deviation $xx
  764. // Bits for milliseconds dev. $xx
  765. // Then for every reference the following data is included;
  766. // Deviation in bytes %xxx....
  767. // Deviation in milliseconds %xxx....
  768. $frame_offset = 0;
  769. $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
  770. $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
  771. $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
  772. $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
  773. $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
  774. $parsedFrame['data'] = substr($parsedFrame['data'], 10);
  775. while ($frame_offset < strlen($parsedFrame['data'])) {
  776. $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
  777. }
  778. $reference_counter = 0;
  779. while (strlen($deviationbitstream) > 0) {
  780. $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
  781. $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
  782. $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
  783. $reference_counter++;
  784. }
  785. unset($parsedFrame['data']);
  786. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes
  787. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes
  788. // There may only be one 'SYTC' frame in each tag
  789. // <Header for 'Synchronised tempo codes', ID: 'SYTC'>
  790. // Time stamp format $xx
  791. // Tempo data <binary data>
  792. // Where time stamp format is:
  793. // $01 (32-bit value) MPEG frames from beginning of file
  794. // $02 (32-bit value) milliseconds from beginning of file
  795. $frame_offset = 0;
  796. $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  797. $timestamp_counter = 0;
  798. while ($frame_offset < strlen($parsedFrame['data'])) {
  799. $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  800. if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
  801. $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
  802. }
  803. $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  804. $frame_offset += 4;
  805. $timestamp_counter++;
  806. }
  807. unset($parsedFrame['data']);
  808. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription
  809. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription
  810. // There may be more than one 'Unsynchronised lyrics/text transcription' frame
  811. // in each tag, but only one with the same language and content descriptor.
  812. // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
  813. // Text encoding $xx
  814. // Language $xx xx xx
  815. // Content descriptor <text string according to encoding> $00 (00)
  816. // Lyrics/text <full text string according to encoding>
  817. $frame_offset = 0;
  818. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  819. $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
  820. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  821. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  822. $frame_textencoding_terminator = "\x00";
  823. }
  824. $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
  825. $frame_offset += 3;
  826. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  827. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  828. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  829. }
  830. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  831. if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
  832. // if description only contains a BOM or terminator then make it blank
  833. $frame_description = '';
  834. }
  835. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
  836. $parsedFrame['encodingid'] = $frame_textencoding;
  837. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  838. $parsedFrame['language'] = $frame_language;
  839. $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
  840. $parsedFrame['description'] = $frame_description;
  841. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  842. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  843. }
  844. unset($parsedFrame['data']);
  845. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text
  846. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text
  847. // There may be more than one 'SYLT' frame in each tag,
  848. // but only one with the same language and content descriptor.
  849. // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
  850. // Text encoding $xx
  851. // Language $xx xx xx
  852. // Time stamp format $xx
  853. // $01 (32-bit value) MPEG frames from beginning of file
  854. // $02 (32-bit value) milliseconds from beginning of file
  855. // Content type $xx
  856. // Content descriptor <text string according to encoding> $00 (00)
  857. // Terminated text to be synced (typically a syllable)
  858. // Sync identifier (terminator to above string) $00 (00)
  859. // Time stamp $xx (xx ...)
  860. $frame_offset = 0;
  861. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  862. $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
  863. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  864. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  865. $frame_textencoding_terminator = "\x00";
  866. }
  867. $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
  868. $frame_offset += 3;
  869. $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  870. $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  871. $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
  872. $parsedFrame['encodingid'] = $frame_textencoding;
  873. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  874. $parsedFrame['language'] = $frame_language;
  875. $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
  876. $timestampindex = 0;
  877. $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
  878. while (strlen($frame_remainingdata)) {
  879. $frame_offset = 0;
  880. $frame_terminatorpos = strpos($frame_remainingdata, $frame_textencoding_terminator);
  881. if ($frame_terminatorpos === false) {
  882. $frame_remainingdata = '';
  883. } else {
  884. if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  885. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  886. }
  887. $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
  888. $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
  889. if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
  890. // timestamp probably omitted for first data item
  891. } else {
  892. $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
  893. $frame_remainingdata = substr($frame_remainingdata, 4);
  894. }
  895. $timestampindex++;
  896. }
  897. }
  898. unset($parsedFrame['data']);
  899. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments
  900. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments
  901. // There may be more than one comment frame in each tag,
  902. // but only one with the same language and content descriptor.
  903. // <Header for 'Comment', ID: 'COMM'>
  904. // Text encoding $xx
  905. // Language $xx xx xx
  906. // Short content descrip. <text string according to encoding> $00 (00)
  907. // The actual text <full text string according to encoding>
  908. if (strlen($parsedFrame['data']) < 5) {
  909. $this->warning('Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']);
  910. } else {
  911. $frame_offset = 0;
  912. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  913. $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
  914. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  915. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  916. $frame_textencoding_terminator = "\x00";
  917. }
  918. $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
  919. $frame_offset += 3;
  920. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  921. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  922. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  923. }
  924. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  925. if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
  926. // if description only contains a BOM or terminator then make it blank
  927. $frame_description = '';
  928. }
  929. $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
  930. $parsedFrame['encodingid'] = $frame_textencoding;
  931. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  932. $parsedFrame['language'] = $frame_language;
  933. $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
  934. $parsedFrame['description'] = $frame_description;
  935. $parsedFrame['data'] = $frame_text;
  936. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  937. $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
  938. if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
  939. $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  940. } else {
  941. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  942. }
  943. }
  944. }
  945. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
  946. // There may be more than one 'RVA2' frame in each tag,
  947. // but only one with the same identification string
  948. // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
  949. // Identification <text string> $00
  950. // The 'identification' string is used to identify the situation and/or
  951. // device where this adjustment should apply. The following is then
  952. // repeated for every channel:
  953. // Type of channel $xx
  954. // Volume adjustment $xx xx
  955. // Bits representing peak $xx
  956. // Peak volume $xx (xx ...)
  957. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00");
  958. $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
  959. if (ord($frame_idstring) === 0) {
  960. $frame_idstring = '';
  961. }
  962. $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
  963. $parsedFrame['description'] = $frame_idstring;
  964. $RVA2channelcounter = 0;
  965. while (strlen($frame_remainingdata) >= 5) {
  966. $frame_offset = 0;
  967. $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
  968. $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid;
  969. $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
  970. $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed
  971. $frame_offset += 2;
  972. $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
  973. if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) {
  974. $this->warning('ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value');
  975. break;
  976. }
  977. $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8);
  978. $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
  979. $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
  980. $RVA2channelcounter++;
  981. }
  982. unset($parsedFrame['data']);
  983. } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
  984. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only)
  985. // There may only be one 'RVA' frame in each tag
  986. // <Header for 'Relative volume adjustment', ID: 'RVA'>
  987. // ID3v2.2 => Increment/decrement %000000ba
  988. // ID3v2.3 => Increment/decrement %00fedcba
  989. // Bits used for volume descr. $xx
  990. // Relative volume change, right $xx xx (xx ...) // a
  991. // Relative volume change, left $xx xx (xx ...) // b
  992. // Peak volume right $xx xx (xx ...)
  993. // Peak volume left $xx xx (xx ...)
  994. // ID3v2.3 only, optional (not present in ID3v2.2):
  995. // Relative volume change, right back $xx xx (xx ...) // c
  996. // Relative volume change, left back $xx xx (xx ...) // d
  997. // Peak volume right back $xx xx (xx ...)
  998. // Peak volume left back $xx xx (xx ...)
  999. // ID3v2.3 only, optional (not present in ID3v2.2):
  1000. // Relative volume change, center $xx xx (xx ...) // e
  1001. // Peak volume center $xx xx (xx ...)
  1002. // ID3v2.3 only, optional (not present in ID3v2.2):
  1003. // Relative volume change, bass $xx xx (xx ...) // f
  1004. // Peak volume bass $xx xx (xx ...)
  1005. $frame_offset = 0;
  1006. $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
  1007. $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
  1008. $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
  1009. $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1010. $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
  1011. $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1012. if ($parsedFrame['incdec']['right'] === false) {
  1013. $parsedFrame['volumechange']['right'] *= -1;
  1014. }
  1015. $frame_offset += $frame_bytesvolume;
  1016. $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1017. if ($parsedFrame['incdec']['left'] === false) {
  1018. $parsedFrame['volumechange']['left'] *= -1;
  1019. }
  1020. $frame_offset += $frame_bytesvolume;
  1021. $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1022. $frame_offset += $frame_bytesvolume;
  1023. $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1024. $frame_offset += $frame_bytesvolume;
  1025. if ($id3v2_majorversion == 3) {
  1026. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
  1027. if (strlen($parsedFrame['data']) > 0) {
  1028. $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
  1029. $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
  1030. $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1031. if ($parsedFrame['incdec']['rightrear'] === false) {
  1032. $parsedFrame['volumechange']['rightrear'] *= -1;
  1033. }
  1034. $frame_offset += $frame_bytesvolume;
  1035. $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1036. if ($parsedFrame['incdec']['leftrear'] === false) {
  1037. $parsedFrame['volumechange']['leftrear'] *= -1;
  1038. }
  1039. $frame_offset += $frame_bytesvolume;
  1040. $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1041. $frame_offset += $frame_bytesvolume;
  1042. $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1043. $frame_offset += $frame_bytesvolume;
  1044. }
  1045. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
  1046. if (strlen($parsedFrame['data']) > 0) {
  1047. $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
  1048. $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1049. if ($parsedFrame['incdec']['center'] === false) {
  1050. $parsedFrame['volumechange']['center'] *= -1;
  1051. }
  1052. $frame_offset += $frame_bytesvolume;
  1053. $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1054. $frame_offset += $frame_bytesvolume;
  1055. }
  1056. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
  1057. if (strlen($parsedFrame['data']) > 0) {
  1058. $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
  1059. $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1060. if ($parsedFrame['incdec']['bass'] === false) {
  1061. $parsedFrame['volumechange']['bass'] *= -1;
  1062. }
  1063. $frame_offset += $frame_bytesvolume;
  1064. $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  1065. $frame_offset += $frame_bytesvolume;
  1066. }
  1067. }
  1068. unset($parsedFrame['data']);
  1069. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
  1070. // There may be more than one 'EQU2' frame in each tag,
  1071. // but only one with the same identification string
  1072. // <Header of 'Equalisation (2)', ID: 'EQU2'>
  1073. // Interpolation method $xx
  1074. // $00 Band
  1075. // $01 Linear
  1076. // Identification <text string> $00
  1077. // The following is then repeated for every adjustment point
  1078. // Frequency $xx xx
  1079. // Volume adjustment $xx xx
  1080. $frame_offset = 0;
  1081. $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1082. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1083. $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1084. if (ord($frame_idstring) === 0) {
  1085. $frame_idstring = '';
  1086. }
  1087. $parsedFrame['description'] = $frame_idstring;
  1088. $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
  1089. while (strlen($frame_remainingdata)) {
  1090. $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
  1091. $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
  1092. $frame_remainingdata = substr($frame_remainingdata, 4);
  1093. }
  1094. $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
  1095. unset($parsedFrame['data']);
  1096. } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only)
  1097. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only)
  1098. // There may only be one 'EQUA' frame in each tag
  1099. // <Header for 'Relative volume adjustment', ID: 'EQU'>
  1100. // Adjustment bits $xx
  1101. // This is followed by 2 bytes + ('adjustment bits' rounded up to the
  1102. // nearest byte) for every equalisation band in the following format,
  1103. // giving a frequency range of 0 - 32767Hz:
  1104. // Increment/decrement %x (MSB of the Frequency)
  1105. // Frequency (lower 15 bits)
  1106. // Adjustment $xx (xx ...)
  1107. $frame_offset = 0;
  1108. $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
  1109. $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
  1110. $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
  1111. while (strlen($frame_remainingdata) > 0) {
  1112. $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
  1113. $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
  1114. $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
  1115. $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
  1116. $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
  1117. if ($parsedFrame[$frame_frequency]['incdec'] === false) {
  1118. $parsedFrame[$frame_frequency]['adjustment'] *= -1;
  1119. }
  1120. $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
  1121. }
  1122. unset($parsedFrame['data']);
  1123. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb
  1124. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb
  1125. // There may only be one 'RVRB' frame in each tag.
  1126. // <Header for 'Reverb', ID: 'RVRB'>
  1127. // Reverb left (ms) $xx xx
  1128. // Reverb right (ms) $xx xx
  1129. // Reverb bounces, left $xx
  1130. // Reverb bounces, right $xx
  1131. // Reverb feedback, left to left $xx
  1132. // Reverb feedback, left to right $xx
  1133. // Reverb feedback, right to right $xx
  1134. // Reverb feedback, right to left $xx
  1135. // Premix left to right $xx
  1136. // Premix right to left $xx
  1137. $frame_offset = 0;
  1138. $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1139. $frame_offset += 2;
  1140. $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1141. $frame_offset += 2;
  1142. $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1143. $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1144. $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1145. $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1146. $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1147. $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1148. $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1149. $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1150. unset($parsedFrame['data']);
  1151. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture
  1152. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture
  1153. // There may be several pictures attached to one file,
  1154. // each in their individual 'APIC' frame, but only one
  1155. // with the same content descriptor
  1156. // <Header for 'Attached picture', ID: 'APIC'>
  1157. // Text encoding $xx
  1158. // ID3v2.3+ => MIME type <text string> $00
  1159. // ID3v2.2 => Image format $xx xx xx
  1160. // Picture type $xx
  1161. // Description <text string according to encoding> $00 (00)
  1162. // Picture data <binary data>
  1163. $frame_offset = 0;
  1164. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1165. $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
  1166. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1167. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  1168. $frame_textencoding_terminator = "\x00";
  1169. }
  1170. if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
  1171. $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
  1172. if (strtolower($frame_imagetype) == 'ima') {
  1173. // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
  1174. // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net)
  1175. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1176. $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1177. if (ord($frame_mimetype) === 0) {
  1178. $frame_mimetype = '';
  1179. }
  1180. $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
  1181. if ($frame_imagetype == 'JPEG') {
  1182. $frame_imagetype = 'JPG';
  1183. }
  1184. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1185. } else {
  1186. $frame_offset += 3;
  1187. }
  1188. }
  1189. if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
  1190. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1191. $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1192. if (ord($frame_mimetype) === 0) {
  1193. $frame_mimetype = '';
  1194. }
  1195. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1196. }
  1197. $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1198. if ($frame_offset >= $parsedFrame['datalength']) {
  1199. $this->warning('data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset));
  1200. } else {
  1201. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  1202. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  1203. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1204. }
  1205. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1206. if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
  1207. // if description only contains a BOM or terminator then make it blank
  1208. $frame_description = '';
  1209. }
  1210. $parsedFrame['encodingid'] = $frame_textencoding;
  1211. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1212. if ($id3v2_majorversion == 2) {
  1213. $parsedFrame['imagetype'] = $frame_imagetype;
  1214. } else {
  1215. $parsedFrame['mime'] = $frame_mimetype;
  1216. }
  1217. $parsedFrame['picturetypeid'] = $frame_picturetype;
  1218. $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
  1219. $parsedFrame['description'] = $frame_description;
  1220. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
  1221. $parsedFrame['datalength'] = strlen($parsedFrame['data']);
  1222. $parsedFrame['image_mime'] = '';
  1223. $imageinfo = array();
  1224. if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) {
  1225. if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
  1226. $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
  1227. if ($imagechunkcheck[0]) {
  1228. $parsedFrame['image_width'] = $imagechunkcheck[0];
  1229. }
  1230. if ($imagechunkcheck[1]) {
  1231. $parsedFrame['image_height'] = $imagechunkcheck[1];
  1232. }
  1233. }
  1234. }
  1235. do {
  1236. if ($this->getid3->option_save_attachments === false) {
  1237. // skip entirely
  1238. unset($parsedFrame['data']);
  1239. break;
  1240. }
  1241. if ($this->getid3->option_save_attachments === true) {
  1242. // great
  1243. /*
  1244. } elseif (is_int($this->getid3->option_save_attachments)) {
  1245. if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) {
  1246. // too big, skip
  1247. $this->warning('attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)');
  1248. unset($parsedFrame['data']);
  1249. break;
  1250. }
  1251. */
  1252. } elseif (is_string($this->getid3->option_save_attachments)) {
  1253. $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
  1254. if (!is_dir($dir) || !getID3::is_writable($dir)) {
  1255. // cannot write, skip
  1256. $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)');
  1257. unset($parsedFrame['data']);
  1258. break;
  1259. }
  1260. }
  1261. // if we get this far, must be OK
  1262. if (is_string($this->getid3->option_save_attachments)) {
  1263. $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset;
  1264. if (!file_exists($destination_filename) || getID3::is_writable($destination_filename)) {
  1265. file_put_contents($destination_filename, $parsedFrame['data']);
  1266. } else {
  1267. $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)');
  1268. }
  1269. $parsedFrame['data_filename'] = $destination_filename;
  1270. unset($parsedFrame['data']);
  1271. } else {
  1272. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  1273. if (!isset($info['id3v2']['comments']['picture'])) {
  1274. $info['id3v2']['comments']['picture'] = array();
  1275. }
  1276. $comments_picture_data = array();
  1277. foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
  1278. if (isset($parsedFrame[$picture_key])) {
  1279. $comments_picture_data[$picture_key] = $parsedFrame[$picture_key];
  1280. }
  1281. }
  1282. $info['id3v2']['comments']['picture'][] = $comments_picture_data;
  1283. unset($comments_picture_data);
  1284. }
  1285. }
  1286. } while (false);
  1287. }
  1288. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object
  1289. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object
  1290. // There may be more than one 'GEOB' frame in each tag,
  1291. // but only one with the same content descriptor
  1292. // <Header for 'General encapsulated object', ID: 'GEOB'>
  1293. // Text encoding $xx
  1294. // MIME type <text string> $00
  1295. // Filename <text string according to encoding> $00 (00)
  1296. // Content description <text string according to encoding> $00 (00)
  1297. // Encapsulated object <binary data>
  1298. $frame_offset = 0;
  1299. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1300. $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
  1301. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1302. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  1303. $frame_textencoding_terminator = "\x00";
  1304. }
  1305. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1306. $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1307. if (ord($frame_mimetype) === 0) {
  1308. $frame_mimetype = '';
  1309. }
  1310. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1311. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  1312. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  1313. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1314. }
  1315. $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1316. if (ord($frame_filename) === 0) {
  1317. $frame_filename = '';
  1318. }
  1319. $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
  1320. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  1321. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  1322. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1323. }
  1324. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1325. if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
  1326. // if description only contains a BOM or terminator then make it blank
  1327. $frame_description = '';
  1328. }
  1329. $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
  1330. $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
  1331. $parsedFrame['encodingid'] = $frame_textencoding;
  1332. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1333. $parsedFrame['mime'] = $frame_mimetype;
  1334. $parsedFrame['filename'] = $frame_filename;
  1335. $parsedFrame['description'] = $frame_description;
  1336. unset($parsedFrame['data']);
  1337. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter
  1338. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter
  1339. // There may only be one 'PCNT' frame in each tag.
  1340. // When the counter reaches all one's, one byte is inserted in
  1341. // front of the counter thus making the counter eight bits bigger
  1342. // <Header for 'Play counter', ID: 'PCNT'>
  1343. // Counter $xx xx xx xx (xx ...)
  1344. $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
  1345. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter
  1346. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter
  1347. // There may be more than one 'POPM' frame in each tag,
  1348. // but only one with the same email address
  1349. // <Header for 'Popularimeter', ID: 'POPM'>
  1350. // Email to user <text string> $00
  1351. // Rating $xx
  1352. // Counter $xx xx xx xx (xx ...)
  1353. $frame_offset = 0;
  1354. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1355. $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1356. if (ord($frame_emailaddress) === 0) {
  1357. $frame_emailaddress = '';
  1358. }
  1359. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1360. $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1361. $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
  1362. $parsedFrame['email'] = $frame_emailaddress;
  1363. $parsedFrame['rating'] = $frame_rating;
  1364. unset($parsedFrame['data']);
  1365. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size
  1366. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size
  1367. // There may only be one 'RBUF' frame in each tag
  1368. // <Header for 'Recommended buffer size', ID: 'RBUF'>
  1369. // Buffer size $xx xx xx
  1370. // Embedded info flag %0000000x
  1371. // Offset to next tag $xx xx xx xx
  1372. $frame_offset = 0;
  1373. $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
  1374. $frame_offset += 3;
  1375. $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
  1376. $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
  1377. $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1378. unset($parsedFrame['data']);
  1379. } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only)
  1380. // There may be more than one 'CRM' frame in a tag,
  1381. // but only one with the same 'owner identifier'
  1382. // <Header for 'Encrypted meta frame', ID: 'CRM'>
  1383. // Owner identifier <textstring> $00 (00)
  1384. // Content/explanation <textstring> $00 (00)
  1385. // Encrypted datablock <binary data>
  1386. $frame_offset = 0;
  1387. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1388. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1389. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1390. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1391. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1392. if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
  1393. // if description only contains a BOM or terminator then make it blank
  1394. $frame_description = '';
  1395. }
  1396. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1397. $parsedFrame['ownerid'] = $frame_ownerid;
  1398. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1399. $parsedFrame['description'] = $frame_description;
  1400. unset($parsedFrame['data']);
  1401. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption
  1402. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption
  1403. // There may be more than one 'AENC' frames in a tag,
  1404. // but only one with the same 'Owner identifier'
  1405. // <Header for 'Audio encryption', ID: 'AENC'>
  1406. // Owner identifier <text string> $00
  1407. // Preview start $xx xx
  1408. // Preview length $xx xx
  1409. // Encryption info <binary data>
  1410. $frame_offset = 0;
  1411. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1412. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1413. if (ord($frame_ownerid) === 0) {
  1414. $frame_ownerid = '';
  1415. }
  1416. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1417. $parsedFrame['ownerid'] = $frame_ownerid;
  1418. $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1419. $frame_offset += 2;
  1420. $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1421. $frame_offset += 2;
  1422. $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
  1423. unset($parsedFrame['data']);
  1424. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information
  1425. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information
  1426. // There may be more than one 'LINK' frame in a tag,
  1427. // but only one with the same contents
  1428. // <Header for 'Linked information', ID: 'LINK'>
  1429. // ID3v2.3+ => Frame identifier $xx xx xx xx
  1430. // ID3v2.2 => Frame identifier $xx xx xx
  1431. // URL <text string> $00
  1432. // ID and additional data <text string(s)>
  1433. $frame_offset = 0;
  1434. if ($id3v2_majorversion == 2) {
  1435. $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
  1436. $frame_offset += 3;
  1437. } else {
  1438. $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
  1439. $frame_offset += 4;
  1440. }
  1441. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1442. $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1443. if (ord($frame_url) === 0) {
  1444. $frame_url = '';
  1445. }
  1446. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1447. $parsedFrame['url'] = $frame_url;
  1448. $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
  1449. if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
  1450. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback_iso88591_utf8($parsedFrame['url']);
  1451. }
  1452. unset($parsedFrame['data']);
  1453. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
  1454. // There may only be one 'POSS' frame in each tag
  1455. // <Head for 'Position synchronisation', ID: 'POSS'>
  1456. // Time stamp format $xx
  1457. // Position $xx (xx ...)
  1458. $frame_offset = 0;
  1459. $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1460. $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
  1461. unset($parsedFrame['data']);
  1462. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only)
  1463. // There may be more than one 'Terms of use' frame in a tag,
  1464. // but only one with the same 'Language'
  1465. // <Header for 'Terms of use frame', ID: 'USER'>
  1466. // Text encoding $xx
  1467. // Language $xx xx xx
  1468. // The actual text <text string according to encoding>
  1469. $frame_offset = 0;
  1470. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1471. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1472. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  1473. }
  1474. $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
  1475. $frame_offset += 3;
  1476. $parsedFrame['language'] = $frame_language;
  1477. $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
  1478. $parsedFrame['encodingid'] = $frame_textencoding;
  1479. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1480. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1481. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  1482. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  1483. }
  1484. unset($parsedFrame['data']);
  1485. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only)
  1486. // There may only be one 'OWNE' frame in a tag
  1487. // <Header for 'Ownership frame', ID: 'OWNE'>
  1488. // Text encoding $xx
  1489. // Price paid <text string> $00
  1490. // Date of purch. <text string>
  1491. // Seller <text string according to encoding>
  1492. $frame_offset = 0;
  1493. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1494. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1495. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  1496. }
  1497. $parsedFrame['encodingid'] = $frame_textencoding;
  1498. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1499. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1500. $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1501. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1502. $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
  1503. $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
  1504. $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
  1505. $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
  1506. if ($this->IsValidDateStampString($parsedFrame['purchasedate'])) {
  1507. $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
  1508. }
  1509. $frame_offset += 8;
  1510. $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
  1511. unset($parsedFrame['data']);
  1512. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only)
  1513. // There may be more than one 'commercial frame' in a tag,
  1514. // but no two may be identical
  1515. // <Header for 'Commercial frame', ID: 'COMR'>
  1516. // Text encoding $xx
  1517. // Price string <text string> $00
  1518. // Valid until <text string>
  1519. // Contact URL <text string> $00
  1520. // Received as $xx
  1521. // Name of seller <text string according to encoding> $00 (00)
  1522. // Description <text string according to encoding> $00 (00)
  1523. // Picture MIME type <string> $00
  1524. // Seller logo <binary data>
  1525. $frame_offset = 0;
  1526. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1527. $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
  1528. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1529. $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
  1530. $frame_textencoding_terminator = "\x00";
  1531. }
  1532. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1533. $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1534. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1535. $frame_rawpricearray = explode('/', $frame_pricestring);
  1536. foreach ($frame_rawpricearray as $key => $val) {
  1537. $frame_currencyid = substr($val, 0, 3);
  1538. $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
  1539. $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
  1540. }
  1541. $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
  1542. $frame_offset += 8;
  1543. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1544. $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1545. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1546. $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1547. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  1548. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  1549. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1550. }
  1551. $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1552. if (ord($frame_sellername) === 0) {
  1553. $frame_sellername = '';
  1554. }
  1555. $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
  1556. $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
  1557. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
  1558. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1559. }
  1560. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1561. if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
  1562. // if description only contains a BOM or terminator then make it blank
  1563. $frame_description = '';
  1564. }
  1565. $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
  1566. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1567. $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1568. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1569. $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
  1570. $parsedFrame['encodingid'] = $frame_textencoding;
  1571. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1572. $parsedFrame['pricevaliduntil'] = $frame_datestring;
  1573. $parsedFrame['contacturl'] = $frame_contacturl;
  1574. $parsedFrame['receivedasid'] = $frame_receivedasid;
  1575. $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
  1576. $parsedFrame['sellername'] = $frame_sellername;
  1577. $parsedFrame['description'] = $frame_description;
  1578. $parsedFrame['mime'] = $frame_mimetype;
  1579. $parsedFrame['logo'] = $frame_sellerlogo;
  1580. unset($parsedFrame['data']);
  1581. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only)
  1582. // There may be several 'ENCR' frames in a tag,
  1583. // but only one containing the same symbol
  1584. // and only one containing the same owner identifier
  1585. // <Header for 'Encryption method registration', ID: 'ENCR'>
  1586. // Owner identifier <text string> $00
  1587. // Method symbol $xx
  1588. // Encryption data <binary data>
  1589. $frame_offset = 0;
  1590. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1591. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1592. if (ord($frame_ownerid) === 0) {
  1593. $frame_ownerid = '';
  1594. }
  1595. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1596. $parsedFrame['ownerid'] = $frame_ownerid;
  1597. $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1598. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1599. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only)
  1600. // There may be several 'GRID' frames in a tag,
  1601. // but only one containing the same symbol
  1602. // and only one containing the same owner identifier
  1603. // <Header for 'Group ID registration', ID: 'GRID'>
  1604. // Owner identifier <text string> $00
  1605. // Group symbol $xx
  1606. // Group dependent data <binary data>
  1607. $frame_offset = 0;
  1608. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1609. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1610. if (ord($frame_ownerid) === 0) {
  1611. $frame_ownerid = '';
  1612. }
  1613. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1614. $parsedFrame['ownerid'] = $frame_ownerid;
  1615. $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1616. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1617. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only)
  1618. // The tag may contain more than one 'PRIV' frame
  1619. // but only with different contents
  1620. // <Header for 'Private frame', ID: 'PRIV'>
  1621. // Owner identifier <text string> $00
  1622. // The private data <binary data>
  1623. $frame_offset = 0;
  1624. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1625. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1626. if (ord($frame_ownerid) === 0) {
  1627. $frame_ownerid = '';
  1628. }
  1629. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1630. $parsedFrame['ownerid'] = $frame_ownerid;
  1631. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1632. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only)
  1633. // There may be more than one 'signature frame' in a tag,
  1634. // but no two may be identical
  1635. // <Header for 'Signature frame', ID: 'SIGN'>
  1636. // Group symbol $xx
  1637. // Signature <binary data>
  1638. $frame_offset = 0;
  1639. $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1640. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1641. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only)
  1642. // There may only be one 'seek frame' in a tag
  1643. // <Header for 'Seek frame', ID: 'SEEK'>
  1644. // Minimum offset to next tag $xx xx xx xx
  1645. $frame_offset = 0;
  1646. $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1647. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only)
  1648. // There may only be one 'audio seek point index' frame in a tag
  1649. // <Header for 'Seek Point Index', ID: 'ASPI'>
  1650. // Indexed data start (S) $xx xx xx xx
  1651. // Indexed data length (L) $xx xx xx xx
  1652. // Number of index points (N) $xx xx
  1653. // Bits per index point (b) $xx
  1654. // Then for every index point the following data is included:
  1655. // Fraction at index (Fi) $xx (xx)
  1656. $frame_offset = 0;
  1657. $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1658. $frame_offset += 4;
  1659. $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1660. $frame_offset += 4;
  1661. $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1662. $frame_offset += 2;
  1663. $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1664. $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
  1665. for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) {
  1666. $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
  1667. $frame_offset += $frame_bytesperpoint;
  1668. }
  1669. unset($parsedFrame['data']);
  1670. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment
  1671. // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
  1672. // There may only be one 'RGAD' frame in a tag
  1673. // <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
  1674. // Peak Amplitude $xx $xx $xx $xx
  1675. // Radio Replay Gain Adjustment %aaabbbcd %dddddddd
  1676. // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd
  1677. // a - name code
  1678. // b - originator code
  1679. // c - sign bit
  1680. // d - replay gain adjustment
  1681. $frame_offset = 0;
  1682. $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
  1683. $frame_offset += 4;
  1684. $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
  1685. $frame_offset += 2;
  1686. $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
  1687. $frame_offset += 2;
  1688. $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
  1689. $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
  1690. $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
  1691. $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
  1692. $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
  1693. $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
  1694. $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
  1695. $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
  1696. $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
  1697. $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
  1698. $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
  1699. $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
  1700. $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
  1701. $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
  1702. $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
  1703. $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
  1704. $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
  1705. $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
  1706. $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
  1707. unset($parsedFrame['data']);
  1708. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CHAP')) { // CHAP Chapters frame (ID3v2.3+ only)
  1709. // http://id3.org/id3v2-chapters-1.0
  1710. // <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes)
  1711. // Element ID <text string> $00
  1712. // Start time $xx xx xx xx
  1713. // End time $xx xx xx xx
  1714. // Start offset $xx xx xx xx
  1715. // End offset $xx xx xx xx
  1716. // <Optional embedded sub-frames>
  1717. $frame_offset = 0;
  1718. @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
  1719. $frame_offset += strlen($parsedFrame['element_id']."\x00");
  1720. $parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1721. $frame_offset += 4;
  1722. $parsedFrame['time_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1723. $frame_offset += 4;
  1724. if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
  1725. // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
  1726. $parsedFrame['offset_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1727. }
  1728. $frame_offset += 4;
  1729. if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
  1730. // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
  1731. $parsedFrame['offset_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1732. }
  1733. $frame_offset += 4;
  1734. if ($frame_offset < strlen($parsedFrame['data'])) {
  1735. $parsedFrame['subframes'] = array();
  1736. while ($frame_offset < strlen($parsedFrame['data'])) {
  1737. // <Optional embedded sub-frames>
  1738. $subframe = array();
  1739. $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
  1740. $frame_offset += 4;
  1741. $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1742. $frame_offset += 4;
  1743. $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1744. $frame_offset += 2;
  1745. if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
  1746. $this->warning('CHAP subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)');
  1747. break;
  1748. }
  1749. $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
  1750. $frame_offset += $subframe['size'];
  1751. $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
  1752. $subframe['text'] = substr($subframe_rawdata, 1);
  1753. $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
  1754. $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
  1755. switch (substr($encoding_converted_text, 0, 2)) {
  1756. case "\xFF\xFE":
  1757. case "\xFE\xFF":
  1758. switch (strtoupper($info['id3v2']['encoding'])) {
  1759. case 'ISO-8859-1':
  1760. case 'UTF-8':
  1761. $encoding_converted_text = substr($encoding_converted_text, 2);
  1762. // remove unwanted byte-order-marks
  1763. break;
  1764. default:
  1765. // ignore
  1766. break;
  1767. }
  1768. break;
  1769. default:
  1770. // do not remove BOM
  1771. break;
  1772. }
  1773. if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
  1774. if ($subframe['name'] == 'TIT2') {
  1775. $parsedFrame['chapter_name'] = $encoding_converted_text;
  1776. } elseif ($subframe['name'] == 'TIT3') {
  1777. $parsedFrame['chapter_description'] = $encoding_converted_text;
  1778. }
  1779. $parsedFrame['subframes'][] = $subframe;
  1780. } else {
  1781. $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
  1782. }
  1783. }
  1784. unset($subframe_rawdata, $subframe, $encoding_converted_text);
  1785. }
  1786. $id3v2_chapter_entry = array();
  1787. foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
  1788. if (isset($parsedFrame[$id3v2_chapter_key])) {
  1789. $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
  1790. }
  1791. }
  1792. if (!isset($info['id3v2']['chapters'])) {
  1793. $info['id3v2']['chapters'] = array();
  1794. }
  1795. $info['id3v2']['chapters'][] = $id3v2_chapter_entry;
  1796. unset($id3v2_chapter_entry, $id3v2_chapter_key);
  1797. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)
  1798. // http://id3.org/id3v2-chapters-1.0
  1799. // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes)
  1800. // Element ID <text string> $00
  1801. // CTOC flags %xx
  1802. // Entry count $xx
  1803. // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */
  1804. // <Optional embedded sub-frames>
  1805. $frame_offset = 0;
  1806. @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
  1807. $frame_offset += strlen($parsedFrame['element_id']."\x00");
  1808. $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
  1809. $frame_offset += 1;
  1810. $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));
  1811. $frame_offset += 1;
  1812. $terminator_position = null;
  1813. for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {
  1814. $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1815. $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);
  1816. $frame_offset = $terminator_position + 1;
  1817. }
  1818. $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01);
  1819. $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03);
  1820. unset($ctoc_flags_raw, $terminator_position);
  1821. if ($frame_offset < strlen($parsedFrame['data'])) {
  1822. $parsedFrame['subframes'] = array();
  1823. while ($frame_offset < strlen($parsedFrame['data'])) {
  1824. // <Optional embedded sub-frames>
  1825. $subframe = array();
  1826. $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
  1827. $frame_offset += 4;
  1828. $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1829. $frame_offset += 4;
  1830. $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1831. $frame_offset += 2;
  1832. if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
  1833. $this->warning('CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)');
  1834. break;
  1835. }
  1836. $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
  1837. $frame_offset += $subframe['size'];
  1838. $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
  1839. $subframe['text'] = substr($subframe_rawdata, 1);
  1840. $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
  1841. $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
  1842. switch (substr($encoding_converted_text, 0, 2)) {
  1843. case "\xFF\xFE":
  1844. case "\xFE\xFF":
  1845. switch (strtoupper($info['id3v2']['encoding'])) {
  1846. case 'ISO-8859-1':
  1847. case 'UTF-8':
  1848. $encoding_converted_text = substr($encoding_converted_text, 2);
  1849. // remove unwanted byte-order-marks
  1850. break;
  1851. default:
  1852. // ignore
  1853. break;
  1854. }
  1855. break;
  1856. default:
  1857. // do not remove BOM
  1858. break;
  1859. }
  1860. if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
  1861. if ($subframe['name'] == 'TIT2') {
  1862. $parsedFrame['toc_name'] = $encoding_converted_text;
  1863. } elseif ($subframe['name'] == 'TIT3') {
  1864. $parsedFrame['toc_description'] = $encoding_converted_text;
  1865. }
  1866. $parsedFrame['subframes'][] = $subframe;
  1867. } else {
  1868. $this->warning('ID3v2.CTOC subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
  1869. }
  1870. }
  1871. unset($subframe_rawdata, $subframe, $encoding_converted_text);
  1872. }
  1873. }
  1874. return true;
  1875. }
  1876. public function DeUnsynchronise($data) {
  1877. return str_replace("\xFF\x00", "\xFF", $data);
  1878. }
  1879. public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) {
  1880. static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
  1881. 0x00 => 'No more than 128 frames and 1 MB total tag size',
  1882. 0x01 => 'No more than 64 frames and 128 KB total tag size',
  1883. 0x02 => 'No more than 32 frames and 40 KB total tag size',
  1884. 0x03 => 'No more than 32 frames and 4 KB total tag size',
  1885. );
  1886. return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
  1887. }
  1888. public function LookupExtendedHeaderRestrictionsTextEncodings($index) {
  1889. static $LookupExtendedHeaderRestrictionsTextEncodings = array(
  1890. 0x00 => 'No restrictions',
  1891. 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8',
  1892. );
  1893. return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
  1894. }
  1895. public function LookupExtendedHeaderRestrictionsTextFieldSize($index) {
  1896. static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
  1897. 0x00 => 'No restrictions',
  1898. 0x01 => 'No string is longer than 1024 characters',
  1899. 0x02 => 'No string is longer than 128 characters',
  1900. 0x03 => 'No string is longer than 30 characters',
  1901. );
  1902. return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
  1903. }
  1904. public function LookupExtendedHeaderRestrictionsImageEncoding($index) {
  1905. static $LookupExtendedHeaderRestrictionsImageEncoding = array(
  1906. 0x00 => 'No restrictions',
  1907. 0x01 => 'Images are encoded only with PNG or JPEG',
  1908. );
  1909. return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
  1910. }
  1911. public function LookupExtendedHeaderRestrictionsImageSizeSize($index) {
  1912. static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
  1913. 0x00 => 'No restrictions',
  1914. 0x01 => 'All images are 256x256 pixels or smaller',
  1915. 0x02 => 'All images are 64x64 pixels or smaller',
  1916. 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise',
  1917. );
  1918. return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
  1919. }
  1920. public function LookupCurrencyUnits($currencyid) {
  1921. $begin = __LINE__;
  1922. /** This is not a comment!
  1923. AED Dirhams
  1924. AFA Afghanis
  1925. ALL Leke
  1926. AMD Drams
  1927. ANG Guilders
  1928. AOA Kwanza
  1929. ARS Pesos
  1930. ATS Schillings
  1931. AUD Dollars
  1932. AWG Guilders
  1933. AZM Manats
  1934. BAM Convertible Marka
  1935. BBD Dollars
  1936. BDT Taka
  1937. BEF Francs
  1938. BGL Leva
  1939. BHD Dinars
  1940. BIF Francs
  1941. BMD Dollars
  1942. BND Dollars
  1943. BOB Bolivianos
  1944. BRL Brazil Real
  1945. BSD Dollars
  1946. BTN Ngultrum
  1947. BWP Pulas
  1948. BYR Rubles
  1949. BZD Dollars
  1950. CAD Dollars
  1951. CDF Congolese Francs
  1952. CHF Francs
  1953. CLP Pesos
  1954. CNY Yuan Renminbi
  1955. COP Pesos
  1956. CRC Colones
  1957. CUP Pesos
  1958. CVE Escudos
  1959. CYP Pounds
  1960. CZK Koruny
  1961. DEM Deutsche Marks
  1962. DJF Francs
  1963. DKK Kroner
  1964. DOP Pesos
  1965. DZD Algeria Dinars
  1966. EEK Krooni
  1967. EGP Pounds
  1968. ERN Nakfa
  1969. ESP Pesetas
  1970. ETB Birr
  1971. EUR Euro
  1972. FIM Markkaa
  1973. FJD Dollars
  1974. FKP Pounds
  1975. FRF Francs
  1976. GBP Pounds
  1977. GEL Lari
  1978. GGP Pounds
  1979. GHC Cedis
  1980. GIP Pounds
  1981. GMD Dalasi
  1982. GNF Francs
  1983. GRD Drachmae
  1984. GTQ Quetzales
  1985. GYD Dollars
  1986. HKD Dollars
  1987. HNL Lempiras
  1988. HRK Kuna
  1989. HTG Gourdes
  1990. HUF Forints
  1991. IDR Rupiahs
  1992. IEP Pounds
  1993. ILS New Shekels
  1994. IMP Pounds
  1995. INR Rupees
  1996. IQD Dinars
  1997. IRR Rials
  1998. ISK Kronur
  1999. ITL Lire
  2000. JEP Pounds
  2001. JMD Dollars
  2002. JOD Dinars
  2003. JPY Yen
  2004. KES Shillings
  2005. KGS Soms
  2006. KHR Riels
  2007. KMF Francs
  2008. KPW Won
  2009. KWD Dinars
  2010. KYD Dollars
  2011. KZT Tenge
  2012. LAK Kips
  2013. LBP Pounds
  2014. LKR Rupees
  2015. LRD Dollars
  2016. LSL Maloti
  2017. LTL Litai
  2018. LUF Francs
  2019. LVL Lati
  2020. LYD Dinars
  2021. MAD Dirhams
  2022. MDL Lei
  2023. MGF Malagasy Francs
  2024. MKD Denars
  2025. MMK Kyats
  2026. MNT Tugriks
  2027. MOP Patacas
  2028. MRO Ouguiyas
  2029. MTL Liri
  2030. MUR Rupees
  2031. MVR Rufiyaa
  2032. MWK Kwachas
  2033. MXN Pesos
  2034. MYR Ringgits
  2035. MZM Meticais
  2036. NAD Dollars
  2037. NGN Nairas
  2038. NIO Gold Cordobas
  2039. NLG Guilders
  2040. NOK Krone
  2041. NPR Nepal Rupees
  2042. NZD Dollars
  2043. OMR Rials
  2044. PAB Balboa
  2045. PEN Nuevos Soles
  2046. PGK Kina
  2047. PHP Pesos
  2048. PKR Rupees
  2049. PLN Zlotych
  2050. PTE Escudos
  2051. PYG Guarani
  2052. QAR Rials
  2053. ROL Lei
  2054. RUR Rubles
  2055. RWF Rwanda Francs
  2056. SAR Riyals
  2057. SBD Dollars
  2058. SCR Rupees
  2059. SDD Dinars
  2060. SEK Kronor
  2061. SGD Dollars
  2062. SHP Pounds
  2063. SIT Tolars
  2064. SKK Koruny
  2065. SLL Leones
  2066. SOS Shillings
  2067. SPL Luigini
  2068. SRG Guilders
  2069. STD Dobras
  2070. SVC Colones
  2071. SYP Pounds
  2072. SZL Emalangeni
  2073. THB Baht
  2074. TJR Rubles
  2075. TMM Manats
  2076. TND Dinars
  2077. TOP Pa'anga
  2078. TRL Liras
  2079. TTD Dollars
  2080. TVD Tuvalu Dollars
  2081. TWD New Dollars
  2082. TZS Shillings
  2083. UAH Hryvnia
  2084. UGX Shillings
  2085. USD Dollars
  2086. UYU Pesos
  2087. UZS Sums
  2088. VAL Lire
  2089. VEB Bolivares
  2090. VND Dong
  2091. VUV Vatu
  2092. WST Tala
  2093. XAF Francs
  2094. XAG Ounces
  2095. XAU Ounces
  2096. XCD Dollars
  2097. XDR Special Drawing Rights
  2098. XPD Ounces
  2099. XPF Francs
  2100. XPT Ounces
  2101. YER Rials
  2102. YUM New Dinars
  2103. ZAR Rand
  2104. ZMK Kwacha
  2105. ZWD Zimbabwe Dollars
  2106. */
  2107. return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
  2108. }
  2109. public function LookupCurrencyCountry($currencyid) {
  2110. $begin = __LINE__;
  2111. /** This is not a comment!
  2112. AED United Arab Emirates
  2113. AFA Afghanistan
  2114. ALL Albania
  2115. AMD Armenia
  2116. ANG Netherlands Antilles
  2117. AOA Angola
  2118. ARS Argentina
  2119. ATS Austria
  2120. AUD Australia
  2121. AWG Aruba
  2122. AZM Azerbaijan
  2123. BAM Bosnia and Herzegovina
  2124. BBD Barbados
  2125. BDT Bangladesh
  2126. BEF Belgium
  2127. BGL Bulgaria
  2128. BHD Bahrain
  2129. BIF Burundi
  2130. BMD Bermuda
  2131. BND Brunei Darussalam
  2132. BOB Bolivia
  2133. BRL Brazil
  2134. BSD Bahamas
  2135. BTN Bhutan
  2136. BWP Botswana
  2137. BYR Belarus
  2138. BZD Belize
  2139. CAD Canada
  2140. CDF Congo/Kinshasa
  2141. CHF Switzerland
  2142. CLP Chile
  2143. CNY China
  2144. COP Colombia
  2145. CRC Costa Rica
  2146. CUP Cuba
  2147. CVE Cape Verde
  2148. CYP Cyprus
  2149. CZK Czech Republic
  2150. DEM Germany
  2151. DJF Djibouti
  2152. DKK Denmark
  2153. DOP Dominican Republic
  2154. DZD Algeria
  2155. EEK Estonia
  2156. EGP Egypt
  2157. ERN Eritrea
  2158. ESP Spain
  2159. ETB Ethiopia
  2160. EUR Euro Member Countries
  2161. FIM Finland
  2162. FJD Fiji
  2163. FKP Falkland Islands (Malvinas)
  2164. FRF France
  2165. GBP United Kingdom
  2166. GEL Georgia
  2167. GGP Guernsey
  2168. GHC Ghana
  2169. GIP Gibraltar
  2170. GMD Gambia
  2171. GNF Guinea
  2172. GRD Greece
  2173. GTQ Guatemala
  2174. GYD Guyana
  2175. HKD Hong Kong
  2176. HNL Honduras
  2177. HRK Croatia
  2178. HTG Haiti
  2179. HUF Hungary
  2180. IDR Indonesia
  2181. IEP Ireland (Eire)
  2182. ILS Israel
  2183. IMP Isle of Man
  2184. INR India
  2185. IQD Iraq
  2186. IRR Iran
  2187. ISK Iceland
  2188. ITL Italy
  2189. JEP Jersey
  2190. JMD Jamaica
  2191. JOD Jordan
  2192. JPY Japan
  2193. KES Kenya
  2194. KGS Kyrgyzstan
  2195. KHR Cambodia
  2196. KMF Comoros
  2197. KPW Korea
  2198. KWD Kuwait
  2199. KYD Cayman Islands
  2200. KZT Kazakstan
  2201. LAK Laos
  2202. LBP Lebanon
  2203. LKR Sri Lanka
  2204. LRD Liberia
  2205. LSL Lesotho
  2206. LTL Lithuania
  2207. LUF Luxembourg
  2208. LVL Latvia
  2209. LYD Libya
  2210. MAD Morocco
  2211. MDL Moldova
  2212. MGF Madagascar
  2213. MKD Macedonia
  2214. MMK Myanmar (Burma)
  2215. MNT Mongolia
  2216. MOP Macau
  2217. MRO Mauritania
  2218. MTL Malta
  2219. MUR Mauritius
  2220. MVR Maldives (Maldive Islands)
  2221. MWK Malawi
  2222. MXN Mexico
  2223. MYR Malaysia
  2224. MZM Mozambique
  2225. NAD Namibia
  2226. NGN Nigeria
  2227. NIO Nicaragua
  2228. NLG Netherlands (Holland)
  2229. NOK Norway
  2230. NPR Nepal
  2231. NZD New Zealand
  2232. OMR Oman
  2233. PAB Panama
  2234. PEN Peru
  2235. PGK Papua New Guinea
  2236. PHP Philippines
  2237. PKR Pakistan
  2238. PLN Poland
  2239. PTE Portugal
  2240. PYG Paraguay
  2241. QAR Qatar
  2242. ROL Romania
  2243. RUR Russia
  2244. RWF Rwanda
  2245. SAR Saudi Arabia
  2246. SBD Solomon Islands
  2247. SCR Seychelles
  2248. SDD Sudan
  2249. SEK Sweden
  2250. SGD Singapore
  2251. SHP Saint Helena
  2252. SIT Slovenia
  2253. SKK Slovakia
  2254. SLL Sierra Leone
  2255. SOS Somalia
  2256. SPL Seborga
  2257. SRG Suriname
  2258. STD São Tome and Principe
  2259. SVC El Salvador
  2260. SYP Syria
  2261. SZL Swaziland
  2262. THB Thailand
  2263. TJR Tajikistan
  2264. TMM Turkmenistan
  2265. TND Tunisia
  2266. TOP Tonga
  2267. TRL Turkey
  2268. TTD Trinidad and Tobago
  2269. TVD Tuvalu
  2270. TWD Taiwan
  2271. TZS Tanzania
  2272. UAH Ukraine
  2273. UGX Uganda
  2274. USD United States of America
  2275. UYU Uruguay
  2276. UZS Uzbekistan
  2277. VAL Vatican City
  2278. VEB Venezuela
  2279. VND Viet Nam
  2280. VUV Vanuatu
  2281. WST Samoa
  2282. XAF Communauté Financière Africaine
  2283. XAG Silver
  2284. XAU Gold
  2285. XCD East Caribbean
  2286. XDR International Monetary Fund
  2287. XPD Palladium
  2288. XPF Comptoirs Français du Pacifique
  2289. XPT Platinum
  2290. YER Yemen
  2291. YUM Yugoslavia
  2292. ZAR South Africa
  2293. ZMK Zambia
  2294. ZWD Zimbabwe
  2295. */
  2296. return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
  2297. }
  2298. public static function LanguageLookup($languagecode, $casesensitive=false) {
  2299. if (!$casesensitive) {
  2300. $languagecode = strtolower($languagecode);
  2301. }
  2302. // http://www.id3.org/id3v2.4.0-structure.txt
  2303. // [4. ID3v2 frame overview]
  2304. // The three byte language field, present in several frames, is used to
  2305. // describe the language of the frame's content, according to ISO-639-2
  2306. // [ISO-639-2]. The language should be represented in lower case. If the
  2307. // language is not known the string "XXX" should be used.
  2308. // ISO 639-2 - http://www.id3.org/iso639-2.html
  2309. $begin = __LINE__;
  2310. /** This is not a comment!
  2311. XXX unknown
  2312. xxx unknown
  2313. aar Afar
  2314. abk Abkhazian
  2315. ace Achinese
  2316. ach Acoli
  2317. ada Adangme
  2318. afa Afro-Asiatic (Other)
  2319. afh Afrihili
  2320. afr Afrikaans
  2321. aka Akan
  2322. akk Akkadian
  2323. alb Albanian
  2324. ale Aleut
  2325. alg Algonquian Languages
  2326. amh Amharic
  2327. ang English, Old (ca. 450-1100)
  2328. apa Apache Languages
  2329. ara Arabic
  2330. arc Aramaic
  2331. arm Armenian
  2332. arn Araucanian
  2333. arp Arapaho
  2334. art Artificial (Other)
  2335. arw Arawak
  2336. asm Assamese
  2337. ath Athapascan Languages
  2338. ava Avaric
  2339. ave Avestan
  2340. awa Awadhi
  2341. aym Aymara
  2342. aze Azerbaijani
  2343. bad Banda
  2344. bai Bamileke Languages
  2345. bak Bashkir
  2346. bal Baluchi
  2347. bam Bambara
  2348. ban Balinese
  2349. baq Basque
  2350. bas Basa
  2351. bat Baltic (Other)
  2352. bej Beja
  2353. bel Byelorussian
  2354. bem Bemba
  2355. ben Bengali
  2356. ber Berber (Other)
  2357. bho Bhojpuri
  2358. bih Bihari
  2359. bik Bikol
  2360. bin Bini
  2361. bis Bislama
  2362. bla Siksika
  2363. bnt Bantu (Other)
  2364. bod Tibetan
  2365. bra Braj
  2366. bre Breton
  2367. bua Buriat
  2368. bug Buginese
  2369. bul Bulgarian
  2370. bur Burmese
  2371. cad Caddo
  2372. cai Central American Indian (Other)
  2373. car Carib
  2374. cat Catalan
  2375. cau Caucasian (Other)
  2376. ceb Cebuano
  2377. cel Celtic (Other)
  2378. ces Czech
  2379. cha Chamorro
  2380. chb Chibcha
  2381. che Chechen
  2382. chg Chagatai
  2383. chi Chinese
  2384. chm Mari
  2385. chn Chinook jargon
  2386. cho Choctaw
  2387. chr Cherokee
  2388. chu Church Slavic
  2389. chv Chuvash
  2390. chy Cheyenne
  2391. cop Coptic
  2392. cor Cornish
  2393. cos Corsican
  2394. cpe Creoles and Pidgins, English-based (Other)
  2395. cpf Creoles and Pidgins, French-based (Other)
  2396. cpp Creoles and Pidgins, Portuguese-based (Other)
  2397. cre Cree
  2398. crp Creoles and Pidgins (Other)
  2399. cus Cushitic (Other)
  2400. cym Welsh
  2401. cze Czech
  2402. dak Dakota
  2403. dan Danish
  2404. del Delaware
  2405. deu German
  2406. din Dinka
  2407. div Divehi
  2408. doi Dogri
  2409. dra Dravidian (Other)
  2410. dua Duala
  2411. dum Dutch, Middle (ca. 1050-1350)
  2412. dut Dutch
  2413. dyu Dyula
  2414. dzo Dzongkha
  2415. efi Efik
  2416. egy Egyptian (Ancient)
  2417. eka Ekajuk
  2418. ell Greek, Modern (1453-)
  2419. elx Elamite
  2420. eng English
  2421. enm English, Middle (ca. 1100-1500)
  2422. epo Esperanto
  2423. esk Eskimo (Other)
  2424. esl Spanish
  2425. est Estonian
  2426. eus Basque
  2427. ewe Ewe
  2428. ewo Ewondo
  2429. fan Fang
  2430. fao Faroese
  2431. fas Persian
  2432. fat Fanti
  2433. fij Fijian
  2434. fin Finnish
  2435. fiu Finno-Ugrian (Other)
  2436. fon Fon
  2437. fra French
  2438. fre French
  2439. frm French, Middle (ca. 1400-1600)
  2440. fro French, Old (842- ca. 1400)
  2441. fry Frisian
  2442. ful Fulah
  2443. gaa Ga
  2444. gae Gaelic (Scots)
  2445. gai Irish
  2446. gay Gayo
  2447. gdh Gaelic (Scots)
  2448. gem Germanic (Other)
  2449. geo Georgian
  2450. ger German
  2451. gez Geez
  2452. gil Gilbertese
  2453. glg Gallegan
  2454. gmh German, Middle High (ca. 1050-1500)
  2455. goh German, Old High (ca. 750-1050)
  2456. gon Gondi
  2457. got Gothic
  2458. grb Grebo
  2459. grc Greek, Ancient (to 1453)
  2460. gre Greek, Modern (1453-)
  2461. grn Guarani
  2462. guj Gujarati
  2463. hai Haida
  2464. hau Hausa
  2465. haw Hawaiian
  2466. heb Hebrew
  2467. her Herero
  2468. hil Hiligaynon
  2469. him Himachali
  2470. hin Hindi
  2471. hmo Hiri Motu
  2472. hun Hungarian
  2473. hup Hupa
  2474. hye Armenian
  2475. iba Iban
  2476. ibo Igbo
  2477. ice Icelandic
  2478. ijo Ijo
  2479. iku Inuktitut
  2480. ilo Iloko
  2481. ina Interlingua (International Auxiliary language Association)
  2482. inc Indic (Other)
  2483. ind Indonesian
  2484. ine Indo-European (Other)
  2485. ine Interlingue
  2486. ipk Inupiak
  2487. ira Iranian (Other)
  2488. iri Irish
  2489. iro Iroquoian uages
  2490. isl Icelandic
  2491. ita Italian
  2492. jav Javanese
  2493. jaw Javanese
  2494. jpn Japanese
  2495. jpr Judeo-Persian
  2496. jrb Judeo-Arabic
  2497. kaa Kara-Kalpak
  2498. kab Kabyle
  2499. kac Kachin
  2500. kal Greenlandic
  2501. kam Kamba
  2502. kan Kannada
  2503. kar Karen
  2504. kas Kashmiri
  2505. kat Georgian
  2506. kau Kanuri
  2507. kaw Kawi
  2508. kaz Kazakh
  2509. kha Khasi
  2510. khi Khoisan (Other)
  2511. khm Khmer
  2512. kho Khotanese
  2513. kik Kikuyu
  2514. kin Kinyarwanda
  2515. kir Kirghiz
  2516. kok Konkani
  2517. kom Komi
  2518. kon Kongo
  2519. kor Korean
  2520. kpe Kpelle
  2521. kro Kru
  2522. kru Kurukh
  2523. kua Kuanyama
  2524. kum Kumyk
  2525. kur Kurdish
  2526. kus Kusaie
  2527. kut Kutenai
  2528. lad Ladino
  2529. lah Lahnda
  2530. lam Lamba
  2531. lao Lao
  2532. lat Latin
  2533. lav Latvian
  2534. lez Lezghian
  2535. lin Lingala
  2536. lit Lithuanian
  2537. lol Mongo
  2538. loz Lozi
  2539. ltz Letzeburgesch
  2540. lub Luba-Katanga
  2541. lug Ganda
  2542. lui Luiseno
  2543. lun Lunda
  2544. luo Luo (Kenya and Tanzania)
  2545. mac Macedonian
  2546. mad Madurese
  2547. mag Magahi
  2548. mah Marshall
  2549. mai Maithili
  2550. mak Macedonian
  2551. mak Makasar
  2552. mal Malayalam
  2553. man Mandingo
  2554. mao Maori
  2555. map Austronesian (Other)
  2556. mar Marathi
  2557. mas Masai
  2558. max Manx
  2559. may Malay
  2560. men Mende
  2561. mga Irish, Middle (900 - 1200)
  2562. mic Micmac
  2563. min Minangkabau
  2564. mis Miscellaneous (Other)
  2565. mkh Mon-Kmer (Other)
  2566. mlg Malagasy
  2567. mlt Maltese
  2568. mni Manipuri
  2569. mno Manobo Languages
  2570. moh Mohawk
  2571. mol Moldavian
  2572. mon Mongolian
  2573. mos Mossi
  2574. mri Maori
  2575. msa Malay
  2576. mul Multiple Languages
  2577. mun Munda Languages
  2578. mus Creek
  2579. mwr Marwari
  2580. mya Burmese
  2581. myn Mayan Languages
  2582. nah Aztec
  2583. nai North American Indian (Other)
  2584. nau Nauru
  2585. nav Navajo
  2586. nbl Ndebele, South
  2587. nde Ndebele, North
  2588. ndo Ndongo
  2589. nep Nepali
  2590. new Newari
  2591. nic Niger-Kordofanian (Other)
  2592. niu Niuean
  2593. nla Dutch
  2594. nno Norwegian (Nynorsk)
  2595. non Norse, Old
  2596. nor Norwegian
  2597. nso Sotho, Northern
  2598. nub Nubian Languages
  2599. nya Nyanja
  2600. nym Nyamwezi
  2601. nyn Nyankole
  2602. nyo Nyoro
  2603. nzi Nzima
  2604. oci Langue d'Oc (post 1500)
  2605. oji Ojibwa
  2606. ori Oriya
  2607. orm Oromo
  2608. osa Osage
  2609. oss Ossetic
  2610. ota Turkish, Ottoman (1500 - 1928)
  2611. oto Otomian Languages
  2612. paa Papuan-Australian (Other)
  2613. pag Pangasinan
  2614. pal Pahlavi
  2615. pam Pampanga
  2616. pan Panjabi
  2617. pap Papiamento
  2618. pau Palauan
  2619. peo Persian, Old (ca 600 - 400 B.C.)
  2620. per Persian
  2621. phn Phoenician
  2622. pli Pali
  2623. pol Polish
  2624. pon Ponape
  2625. por Portuguese
  2626. pra Prakrit uages
  2627. pro Provencal, Old (to 1500)
  2628. pus Pushto
  2629. que Quechua
  2630. raj Rajasthani
  2631. rar Rarotongan
  2632. roa Romance (Other)
  2633. roh Rhaeto-Romance
  2634. rom Romany
  2635. ron Romanian
  2636. rum Romanian
  2637. run Rundi
  2638. rus Russian
  2639. sad Sandawe
  2640. sag Sango
  2641. sah Yakut
  2642. sai South American Indian (Other)
  2643. sal Salishan Languages
  2644. sam Samaritan Aramaic
  2645. san Sanskrit
  2646. sco Scots
  2647. scr Serbo-Croatian
  2648. sel Selkup
  2649. sem Semitic (Other)
  2650. sga Irish, Old (to 900)
  2651. shn Shan
  2652. sid Sidamo
  2653. sin Singhalese
  2654. sio Siouan Languages
  2655. sit Sino-Tibetan (Other)
  2656. sla Slavic (Other)
  2657. slk Slovak
  2658. slo Slovak
  2659. slv Slovenian
  2660. smi Sami Languages
  2661. smo Samoan
  2662. sna Shona
  2663. snd Sindhi
  2664. sog Sogdian
  2665. som Somali
  2666. son Songhai
  2667. sot Sotho, Southern
  2668. spa Spanish
  2669. sqi Albanian
  2670. srd Sardinian
  2671. srr Serer
  2672. ssa Nilo-Saharan (Other)
  2673. ssw Siswant
  2674. ssw Swazi
  2675. suk Sukuma
  2676. sun Sudanese
  2677. sus Susu
  2678. sux Sumerian
  2679. sve Swedish
  2680. swa Swahili
  2681. swe Swedish
  2682. syr Syriac
  2683. tah Tahitian
  2684. tam Tamil
  2685. tat Tatar
  2686. tel Telugu
  2687. tem Timne
  2688. ter Tereno
  2689. tgk Tajik
  2690. tgl Tagalog
  2691. tha Thai
  2692. tib Tibetan
  2693. tig Tigre
  2694. tir Tigrinya
  2695. tiv Tivi
  2696. tli Tlingit
  2697. tmh Tamashek
  2698. tog Tonga (Nyasa)
  2699. ton Tonga (Tonga Islands)
  2700. tru Truk
  2701. tsi Tsimshian
  2702. tsn Tswana
  2703. tso Tsonga
  2704. tuk Turkmen
  2705. tum Tumbuka
  2706. tur Turkish
  2707. tut Altaic (Other)
  2708. twi Twi
  2709. tyv Tuvinian
  2710. uga Ugaritic
  2711. uig Uighur
  2712. ukr Ukrainian
  2713. umb Umbundu
  2714. und Undetermined
  2715. urd Urdu
  2716. uzb Uzbek
  2717. vai Vai
  2718. ven Venda
  2719. vie Vietnamese
  2720. vol Volapük
  2721. vot Votic
  2722. wak Wakashan Languages
  2723. wal Walamo
  2724. war Waray
  2725. was Washo
  2726. wel Welsh
  2727. wen Sorbian Languages
  2728. wol Wolof
  2729. xho Xhosa
  2730. yao Yao
  2731. yap Yap
  2732. yid Yiddish
  2733. yor Yoruba
  2734. zap Zapotec
  2735. zen Zenaga
  2736. zha Zhuang
  2737. zho Chinese
  2738. zul Zulu
  2739. zun Zuni
  2740. */
  2741. return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
  2742. }
  2743. public static function ETCOEventLookup($index) {
  2744. if (($index >= 0x17) && ($index <= 0xDF)) {
  2745. return 'reserved for future use';
  2746. }
  2747. if (($index >= 0xE0) && ($index <= 0xEF)) {
  2748. return 'not predefined synch 0-F';
  2749. }
  2750. if (($index >= 0xF0) && ($index <= 0xFC)) {
  2751. return 'reserved for future use';
  2752. }
  2753. static $EventLookup = array(
  2754. 0x00 => 'padding (has no meaning)',
  2755. 0x01 => 'end of initial silence',
  2756. 0x02 => 'intro start',
  2757. 0x03 => 'main part start',
  2758. 0x04 => 'outro start',
  2759. 0x05 => 'outro end',
  2760. 0x06 => 'verse start',
  2761. 0x07 => 'refrain start',
  2762. 0x08 => 'interlude start',
  2763. 0x09 => 'theme start',
  2764. 0x0A => 'variation start',
  2765. 0x0B => 'key change',
  2766. 0x0C => 'time change',
  2767. 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)',
  2768. 0x0E => 'sustained noise',
  2769. 0x0F => 'sustained noise end',
  2770. 0x10 => 'intro end',
  2771. 0x11 => 'main part end',
  2772. 0x12 => 'verse end',
  2773. 0x13 => 'refrain end',
  2774. 0x14 => 'theme end',
  2775. 0x15 => 'profanity',
  2776. 0x16 => 'profanity end',
  2777. 0xFD => 'audio end (start of silence)',
  2778. 0xFE => 'audio file ends',
  2779. 0xFF => 'one more byte of events follows'
  2780. );
  2781. return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
  2782. }
  2783. public static function SYTLContentTypeLookup($index) {
  2784. static $SYTLContentTypeLookup = array(
  2785. 0x00 => 'other',
  2786. 0x01 => 'lyrics',
  2787. 0x02 => 'text transcription',
  2788. 0x03 => 'movement/part name', // (e.g. 'Adagio')
  2789. 0x04 => 'events', // (e.g. 'Don Quijote enters the stage')
  2790. 0x05 => 'chord', // (e.g. 'Bb F Fsus')
  2791. 0x06 => 'trivia/\'pop up\' information',
  2792. 0x07 => 'URLs to webpages',
  2793. 0x08 => 'URLs to images'
  2794. );
  2795. return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
  2796. }
  2797. public static function APICPictureTypeLookup($index, $returnarray=false) {
  2798. static $APICPictureTypeLookup = array(
  2799. 0x00 => 'Other',
  2800. 0x01 => '32x32 pixels \'file icon\' (PNG only)',
  2801. 0x02 => 'Other file icon',
  2802. 0x03 => 'Cover (front)',
  2803. 0x04 => 'Cover (back)',
  2804. 0x05 => 'Leaflet page',
  2805. 0x06 => 'Media (e.g. label side of CD)',
  2806. 0x07 => 'Lead artist/lead performer/soloist',
  2807. 0x08 => 'Artist/performer',
  2808. 0x09 => 'Conductor',
  2809. 0x0A => 'Band/Orchestra',
  2810. 0x0B => 'Composer',
  2811. 0x0C => 'Lyricist/text writer',
  2812. 0x0D => 'Recording Location',
  2813. 0x0E => 'During recording',
  2814. 0x0F => 'During performance',
  2815. 0x10 => 'Movie/video screen capture',
  2816. 0x11 => 'A bright coloured fish',
  2817. 0x12 => 'Illustration',
  2818. 0x13 => 'Band/artist logotype',
  2819. 0x14 => 'Publisher/Studio logotype'
  2820. );
  2821. if ($returnarray) {
  2822. return $APICPictureTypeLookup;
  2823. }
  2824. return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
  2825. }
  2826. public static function COMRReceivedAsLookup($index) {
  2827. static $COMRReceivedAsLookup = array(
  2828. 0x00 => 'Other',
  2829. 0x01 => 'Standard CD album with other songs',
  2830. 0x02 => 'Compressed audio on CD',
  2831. 0x03 => 'File over the Internet',
  2832. 0x04 => 'Stream over the Internet',
  2833. 0x05 => 'As note sheets',
  2834. 0x06 => 'As note sheets in a book with other sheets',
  2835. 0x07 => 'Music on other media',
  2836. 0x08 => 'Non-musical merchandise'
  2837. );
  2838. return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
  2839. }
  2840. public static function RVA2ChannelTypeLookup($index) {
  2841. static $RVA2ChannelTypeLookup = array(
  2842. 0x00 => 'Other',
  2843. 0x01 => 'Master volume',
  2844. 0x02 => 'Front right',
  2845. 0x03 => 'Front left',
  2846. 0x04 => 'Back right',
  2847. 0x05 => 'Back left',
  2848. 0x06 => 'Front centre',
  2849. 0x07 => 'Back centre',
  2850. 0x08 => 'Subwoofer'
  2851. );
  2852. return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
  2853. }
  2854. public static function FrameNameLongLookup($framename) {
  2855. $begin = __LINE__;
  2856. /** This is not a comment!
  2857. AENC Audio encryption
  2858. APIC Attached picture
  2859. ASPI Audio seek point index
  2860. BUF Recommended buffer size
  2861. CNT Play counter
  2862. COM Comments
  2863. COMM Comments
  2864. COMR Commercial frame
  2865. CRA Audio encryption
  2866. CRM Encrypted meta frame
  2867. ENCR Encryption method registration
  2868. EQU Equalisation
  2869. EQU2 Equalisation (2)
  2870. EQUA Equalisation
  2871. ETC Event timing codes
  2872. ETCO Event timing codes
  2873. GEO General encapsulated object
  2874. GEOB General encapsulated object
  2875. GRID Group identification registration
  2876. IPL Involved people list
  2877. IPLS Involved people list
  2878. LINK Linked information
  2879. LNK Linked information
  2880. MCDI Music CD identifier
  2881. MCI Music CD Identifier
  2882. MLL MPEG location lookup table
  2883. MLLT MPEG location lookup table
  2884. OWNE Ownership frame
  2885. PCNT Play counter
  2886. PIC Attached picture
  2887. POP Popularimeter
  2888. POPM Popularimeter
  2889. POSS Position synchronisation frame
  2890. PRIV Private frame
  2891. RBUF Recommended buffer size
  2892. REV Reverb
  2893. RVA Relative volume adjustment
  2894. RVA2 Relative volume adjustment (2)
  2895. RVAD Relative volume adjustment
  2896. RVRB Reverb
  2897. SEEK Seek frame
  2898. SIGN Signature frame
  2899. SLT Synchronised lyric/text
  2900. STC Synced tempo codes
  2901. SYLT Synchronised lyric/text
  2902. SYTC Synchronised tempo codes
  2903. TAL Album/Movie/Show title
  2904. TALB Album/Movie/Show title
  2905. TBP BPM (Beats Per Minute)
  2906. TBPM BPM (beats per minute)
  2907. TCM Composer
  2908. TCMP Part of a compilation
  2909. TCO Content type
  2910. TCOM Composer
  2911. TCON Content type
  2912. TCOP Copyright message
  2913. TCP Part of a compilation
  2914. TCR Copyright message
  2915. TDA Date
  2916. TDAT Date
  2917. TDEN Encoding time
  2918. TDLY Playlist delay
  2919. TDOR Original release time
  2920. TDRC Recording time
  2921. TDRL Release time
  2922. TDTG Tagging time
  2923. TDY Playlist delay
  2924. TEN Encoded by
  2925. TENC Encoded by
  2926. TEXT Lyricist/Text writer
  2927. TFLT File type
  2928. TFT File type
  2929. TIM Time
  2930. TIME Time
  2931. TIPL Involved people list
  2932. TIT1 Content group description
  2933. TIT2 Title/songname/content description
  2934. TIT3 Subtitle/Description refinement
  2935. TKE Initial key
  2936. TKEY Initial key
  2937. TLA Language(s)
  2938. TLAN Language(s)
  2939. TLE Length
  2940. TLEN Length
  2941. TMCL Musician credits list
  2942. TMED Media type
  2943. TMOO Mood
  2944. TMT Media type
  2945. TOA Original artist(s)/performer(s)
  2946. TOAL Original album/movie/show title
  2947. TOF Original filename
  2948. TOFN Original filename
  2949. TOL Original Lyricist(s)/text writer(s)
  2950. TOLY Original lyricist(s)/text writer(s)
  2951. TOPE Original artist(s)/performer(s)
  2952. TOR Original release year
  2953. TORY Original release year
  2954. TOT Original album/Movie/Show title
  2955. TOWN File owner/licensee
  2956. TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
  2957. TP2 Band/Orchestra/Accompaniment
  2958. TP3 Conductor/Performer refinement
  2959. TP4 Interpreted, remixed, or otherwise modified by
  2960. TPA Part of a set
  2961. TPB Publisher
  2962. TPE1 Lead performer(s)/Soloist(s)
  2963. TPE2 Band/orchestra/accompaniment
  2964. TPE3 Conductor/performer refinement
  2965. TPE4 Interpreted, remixed, or otherwise modified by
  2966. TPOS Part of a set
  2967. TPRO Produced notice
  2968. TPUB Publisher
  2969. TRC ISRC (International Standard Recording Code)
  2970. TRCK Track number/Position in set
  2971. TRD Recording dates
  2972. TRDA Recording dates
  2973. TRK Track number/Position in set
  2974. TRSN Internet radio station name
  2975. TRSO Internet radio station owner
  2976. TS2 Album-Artist sort order
  2977. TSA Album sort order
  2978. TSC Composer sort order
  2979. TSI Size
  2980. TSIZ Size
  2981. TSO2 Album-Artist sort order
  2982. TSOA Album sort order
  2983. TSOC Composer sort order
  2984. TSOP Performer sort order
  2985. TSOT Title sort order
  2986. TSP Performer sort order
  2987. TSRC ISRC (international standard recording code)
  2988. TSS Software/hardware and settings used for encoding
  2989. TSSE Software/Hardware and settings used for encoding
  2990. TSST Set subtitle
  2991. TST Title sort order
  2992. TT1 Content group description
  2993. TT2 Title/Songname/Content description
  2994. TT3 Subtitle/Description refinement
  2995. TXT Lyricist/text writer
  2996. TXX User defined text information frame
  2997. TXXX User defined text information frame
  2998. TYE Year
  2999. TYER Year
  3000. UFI Unique file identifier
  3001. UFID Unique file identifier
  3002. ULT Unsychronised lyric/text transcription
  3003. USER Terms of use
  3004. USLT Unsynchronised lyric/text transcription
  3005. WAF Official audio file webpage
  3006. WAR Official artist/performer webpage
  3007. WAS Official audio source webpage
  3008. WCM Commercial information
  3009. WCOM Commercial information
  3010. WCOP Copyright/Legal information
  3011. WCP Copyright/Legal information
  3012. WOAF Official audio file webpage
  3013. WOAR Official artist/performer webpage
  3014. WOAS Official audio source webpage
  3015. WORS Official Internet radio station homepage
  3016. WPAY Payment
  3017. WPB Publishers official webpage
  3018. WPUB Publishers official webpage
  3019. WXX User defined URL link frame
  3020. WXXX User defined URL link frame
  3021. TFEA Featured Artist
  3022. TSTU Recording Studio
  3023. rgad Replay Gain Adjustment
  3024. */
  3025. return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long');
  3026. // Last three:
  3027. // from Helium2 [www.helium2.com]
  3028. // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
  3029. }
  3030. public static function FrameNameShortLookup($framename) {
  3031. $begin = __LINE__;
  3032. /** This is not a comment!
  3033. AENC audio_encryption
  3034. APIC attached_picture
  3035. ASPI audio_seek_point_index
  3036. BUF recommended_buffer_size
  3037. CNT play_counter
  3038. COM comment
  3039. COMM comment
  3040. COMR commercial_frame
  3041. CRA audio_encryption
  3042. CRM encrypted_meta_frame
  3043. ENCR encryption_method_registration
  3044. EQU equalisation
  3045. EQU2 equalisation
  3046. EQUA equalisation
  3047. ETC event_timing_codes
  3048. ETCO event_timing_codes
  3049. GEO general_encapsulated_object
  3050. GEOB general_encapsulated_object
  3051. GRID group_identification_registration
  3052. IPL involved_people_list
  3053. IPLS involved_people_list
  3054. LINK linked_information
  3055. LNK linked_information
  3056. MCDI music_cd_identifier
  3057. MCI music_cd_identifier
  3058. MLL mpeg_location_lookup_table
  3059. MLLT mpeg_location_lookup_table
  3060. OWNE ownership_frame
  3061. PCNT play_counter
  3062. PIC attached_picture
  3063. POP popularimeter
  3064. POPM popularimeter
  3065. POSS position_synchronisation_frame
  3066. PRIV private_frame
  3067. RBUF recommended_buffer_size
  3068. REV reverb
  3069. RVA relative_volume_adjustment
  3070. RVA2 relative_volume_adjustment
  3071. RVAD relative_volume_adjustment
  3072. RVRB reverb
  3073. SEEK seek_frame
  3074. SIGN signature_frame
  3075. SLT synchronised_lyric
  3076. STC synced_tempo_codes
  3077. SYLT synchronised_lyric
  3078. SYTC synchronised_tempo_codes
  3079. TAL album
  3080. TALB album
  3081. TBP bpm
  3082. TBPM bpm
  3083. TCM composer
  3084. TCMP part_of_a_compilation
  3085. TCO genre
  3086. TCOM composer
  3087. TCON genre
  3088. TCOP copyright_message
  3089. TCP part_of_a_compilation
  3090. TCR copyright_message
  3091. TDA date
  3092. TDAT date
  3093. TDEN encoding_time
  3094. TDLY playlist_delay
  3095. TDOR original_release_time
  3096. TDRC recording_time
  3097. TDRL release_time
  3098. TDTG tagging_time
  3099. TDY playlist_delay
  3100. TEN encoded_by
  3101. TENC encoded_by
  3102. TEXT lyricist
  3103. TFLT file_type
  3104. TFT file_type
  3105. TIM time
  3106. TIME time
  3107. TIPL involved_people_list
  3108. TIT1 content_group_description
  3109. TIT2 title
  3110. TIT3 subtitle
  3111. TKE initial_key
  3112. TKEY initial_key
  3113. TLA language
  3114. TLAN language
  3115. TLE length
  3116. TLEN length
  3117. TMCL musician_credits_list
  3118. TMED media_type
  3119. TMOO mood
  3120. TMT media_type
  3121. TOA original_artist
  3122. TOAL original_album
  3123. TOF original_filename
  3124. TOFN original_filename
  3125. TOL original_lyricist
  3126. TOLY original_lyricist
  3127. TOPE original_artist
  3128. TOR original_year
  3129. TORY original_year
  3130. TOT original_album
  3131. TOWN file_owner
  3132. TP1 artist
  3133. TP2 band
  3134. TP3 conductor
  3135. TP4 remixer
  3136. TPA part_of_a_set
  3137. TPB publisher
  3138. TPE1 artist
  3139. TPE2 band
  3140. TPE3 conductor
  3141. TPE4 remixer
  3142. TPOS part_of_a_set
  3143. TPRO produced_notice
  3144. TPUB publisher
  3145. TRC isrc
  3146. TRCK track_number
  3147. TRD recording_dates
  3148. TRDA recording_dates
  3149. TRK track_number
  3150. TRSN internet_radio_station_name
  3151. TRSO internet_radio_station_owner
  3152. TS2 album_artist_sort_order
  3153. TSA album_sort_order
  3154. TSC composer_sort_order
  3155. TSI size
  3156. TSIZ size
  3157. TSO2 album_artist_sort_order
  3158. TSOA album_sort_order
  3159. TSOC composer_sort_order
  3160. TSOP performer_sort_order
  3161. TSOT title_sort_order
  3162. TSP performer_sort_order
  3163. TSRC isrc
  3164. TSS encoder_settings
  3165. TSSE encoder_settings
  3166. TSST set_subtitle
  3167. TST title_sort_order
  3168. TT1 content_group_description
  3169. TT2 title
  3170. TT3 subtitle
  3171. TXT lyricist
  3172. TXX text
  3173. TXXX text
  3174. TYE year
  3175. TYER year
  3176. UFI unique_file_identifier
  3177. UFID unique_file_identifier
  3178. ULT unsychronised_lyric
  3179. USER terms_of_use
  3180. USLT unsynchronised_lyric
  3181. WAF url_file
  3182. WAR url_artist
  3183. WAS url_source
  3184. WCM commercial_information
  3185. WCOM commercial_information
  3186. WCOP copyright
  3187. WCP copyright
  3188. WOAF url_file
  3189. WOAR url_artist
  3190. WOAS url_source
  3191. WORS url_station
  3192. WPAY url_payment
  3193. WPB url_publisher
  3194. WPUB url_publisher
  3195. WXX url_user
  3196. WXXX url_user
  3197. TFEA featured_artist
  3198. TSTU recording_studio
  3199. rgad replay_gain_adjustment
  3200. */
  3201. return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
  3202. }
  3203. public static function TextEncodingTerminatorLookup($encoding) {
  3204. // http://www.id3.org/id3v2.4.0-structure.txt
  3205. // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
  3206. static $TextEncodingTerminatorLookup = array(
  3207. 0 => "\x00", // $00 ISO-8859-1. Terminated with $00.
  3208. 1 => "\x00\x00", // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
  3209. 2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
  3210. 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
  3211. 255 => "\x00\x00"
  3212. );
  3213. return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : "\x00");
  3214. }
  3215. public static function TextEncodingNameLookup($encoding) {
  3216. // http://www.id3.org/id3v2.4.0-structure.txt
  3217. // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
  3218. static $TextEncodingNameLookup = array(
  3219. 0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00.
  3220. 1 => 'UTF-16', // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
  3221. 2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
  3222. 3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00.
  3223. 255 => 'UTF-16BE'
  3224. );
  3225. return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
  3226. }
  3227. public static function IsValidID3v2FrameName($framename, $id3v2majorversion) {
  3228. switch ($id3v2majorversion) {
  3229. case 2:
  3230. return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
  3231. break;
  3232. case 3:
  3233. case 4:
  3234. return preg_match('#[A-Z][A-Z0-9]{3}#', $framename);
  3235. break;
  3236. }
  3237. return false;
  3238. }
  3239. public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
  3240. for ($i = 0; $i < strlen($numberstring); $i++) {
  3241. if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
  3242. if (($numberstring{$i} == '.') && $allowdecimal) {
  3243. // allowed
  3244. } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
  3245. // allowed
  3246. } else {
  3247. return false;
  3248. }
  3249. }
  3250. }
  3251. return true;
  3252. }
  3253. public static function IsValidDateStampString($datestamp) {
  3254. if (strlen($datestamp) != 8) {
  3255. return false;
  3256. }
  3257. if (!self::IsANumber($datestamp, false)) {
  3258. return false;
  3259. }
  3260. $year = substr($datestamp, 0, 4);
  3261. $month = substr($datestamp, 4, 2);
  3262. $day = substr($datestamp, 6, 2);
  3263. if (($year == 0) || ($month == 0) || ($day == 0)) {
  3264. return false;
  3265. }
  3266. if ($month > 12) {
  3267. return false;
  3268. }
  3269. if ($day > 31) {
  3270. return false;
  3271. }
  3272. if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) {
  3273. return false;
  3274. }
  3275. if (($day > 29) && ($month == 2)) {
  3276. return false;
  3277. }
  3278. return true;
  3279. }
  3280. public static function ID3v2HeaderLength($majorversion) {
  3281. return (($majorversion == 2) ? 6 : 10);
  3282. }
  3283. public static function ID3v22iTunesBrokenFrameName($frame_name) {
  3284. // iTunes (multiple versions) has been known to write ID3v2.3 style frames
  3285. // but use ID3v2.2 frame names, right-padded using either [space] or [null]
  3286. // to make them fit in the 4-byte frame name space of the ID3v2.3 frame.
  3287. // This function will detect and translate the corrupt frame name into ID3v2.3 standard.
  3288. static $ID3v22_iTunes_BrokenFrames = array(
  3289. 'BUF' => 'RBUF', // Recommended buffer size
  3290. 'CNT' => 'PCNT', // Play counter
  3291. 'COM' => 'COMM', // Comments
  3292. 'CRA' => 'AENC', // Audio encryption
  3293. 'EQU' => 'EQUA', // Equalisation
  3294. 'ETC' => 'ETCO', // Event timing codes
  3295. 'GEO' => 'GEOB', // General encapsulated object
  3296. 'IPL' => 'IPLS', // Involved people list
  3297. 'LNK' => 'LINK', // Linked information
  3298. 'MCI' => 'MCDI', // Music CD identifier
  3299. 'MLL' => 'MLLT', // MPEG location lookup table
  3300. 'PIC' => 'APIC', // Attached picture
  3301. 'POP' => 'POPM', // Popularimeter
  3302. 'REV' => 'RVRB', // Reverb
  3303. 'RVA' => 'RVAD', // Relative volume adjustment
  3304. 'SLT' => 'SYLT', // Synchronised lyric/text
  3305. 'STC' => 'SYTC', // Synchronised tempo codes
  3306. 'TAL' => 'TALB', // Album/Movie/Show title
  3307. 'TBP' => 'TBPM', // BPM (beats per minute)
  3308. 'TCM' => 'TCOM', // Composer
  3309. 'TCO' => 'TCON', // Content type
  3310. 'TCP' => 'TCMP', // Part of a compilation
  3311. 'TCR' => 'TCOP', // Copyright message
  3312. 'TDA' => 'TDAT', // Date
  3313. 'TDY' => 'TDLY', // Playlist delay
  3314. 'TEN' => 'TENC', // Encoded by
  3315. 'TFT' => 'TFLT', // File type
  3316. 'TIM' => 'TIME', // Time
  3317. 'TKE' => 'TKEY', // Initial key
  3318. 'TLA' => 'TLAN', // Language(s)
  3319. 'TLE' => 'TLEN', // Length
  3320. 'TMT' => 'TMED', // Media type
  3321. 'TOA' => 'TOPE', // Original artist(s)/performer(s)
  3322. 'TOF' => 'TOFN', // Original filename
  3323. 'TOL' => 'TOLY', // Original lyricist(s)/text writer(s)
  3324. 'TOR' => 'TORY', // Original release year
  3325. 'TOT' => 'TOAL', // Original album/movie/show title
  3326. 'TP1' => 'TPE1', // Lead performer(s)/Soloist(s)
  3327. 'TP2' => 'TPE2', // Band/orchestra/accompaniment
  3328. 'TP3' => 'TPE3', // Conductor/performer refinement
  3329. 'TP4' => 'TPE4', // Interpreted, remixed, or otherwise modified by
  3330. 'TPA' => 'TPOS', // Part of a set
  3331. 'TPB' => 'TPUB', // Publisher
  3332. 'TRC' => 'TSRC', // ISRC (international standard recording code)
  3333. 'TRD' => 'TRDA', // Recording dates
  3334. 'TRK' => 'TRCK', // Track number/Position in set
  3335. 'TS2' => 'TSO2', // Album-Artist sort order
  3336. 'TSA' => 'TSOA', // Album sort order
  3337. 'TSC' => 'TSOC', // Composer sort order
  3338. 'TSI' => 'TSIZ', // Size
  3339. 'TSP' => 'TSOP', // Performer sort order
  3340. 'TSS' => 'TSSE', // Software/Hardware and settings used for encoding
  3341. 'TST' => 'TSOT', // Title sort order
  3342. 'TT1' => 'TIT1', // Content group description
  3343. 'TT2' => 'TIT2', // Title/songname/content description
  3344. 'TT3' => 'TIT3', // Subtitle/Description refinement
  3345. 'TXT' => 'TEXT', // Lyricist/Text writer
  3346. 'TXX' => 'TXXX', // User defined text information frame
  3347. 'TYE' => 'TYER', // Year
  3348. 'UFI' => 'UFID', // Unique file identifier
  3349. 'ULT' => 'USLT', // Unsynchronised lyric/text transcription
  3350. 'WAF' => 'WOAF', // Official audio file webpage
  3351. 'WAR' => 'WOAR', // Official artist/performer webpage
  3352. 'WAS' => 'WOAS', // Official audio source webpage
  3353. 'WCM' => 'WCOM', // Commercial information
  3354. 'WCP' => 'WCOP', // Copyright/Legal information
  3355. 'WPB' => 'WPUB', // Publishers official webpage
  3356. 'WXX' => 'WXXX', // User defined URL link frame
  3357. );
  3358. if (strlen($frame_name) == 4) {
  3359. if ((substr($frame_name, 3, 1) == ' ') || (substr($frame_name, 3, 1) == "\x00")) {
  3360. if (isset($ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)])) {
  3361. return $ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)];
  3362. }
  3363. }
  3364. }
  3365. return false;
  3366. }
  3367. }