module.tag.id3v2.php 151 KB

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