CPI_PlaylistItem.c
上传用户:hxb_1234
上传日期:2010-03-30
资源大小:8328k
文件大小:44k
源码类别:

VC书籍

开发平台:

Visual C++

  1. ////////////////////////////////////////////////////////////////////////////////
  2. #include "stdafx.h"
  3. #include "globals.h"
  4. #include "CPI_Playlist.h"
  5. #include "CPI_PlaylistItem.h"
  6. #include "CPI_PlaylistItem_Internal.h"
  7. #include "CPI_ID3.h"
  8. void CPLI_OGG_SkipOverTab(FILE* pFile);
  9. void CPLI_SetPath(CPs_PlaylistItem* pItem, const char* pcNewPath);
  10. void CPLI_ReadTag_ID3v1(CPs_PlaylistItem* pItem, HANDLE hFile);
  11. void CPLI_ReadTag_ID3v2(CPs_PlaylistItem* pItem, HANDLE hFile);
  12. //void CPLI_ReadTag_OGG(CPs_PlaylistItem* pItem);
  13. void CPLI_WriteTag_ID3v1(CPs_PlaylistItem* pItem, HANDLE hFile);
  14. void CPLI_WriteTag_ID3v2(CPs_PlaylistItem* pItem, HANDLE hFile);
  15. //void CPLI_WriteTag_OGG(CPs_PlaylistItem* pItem, HANDLE hFile);
  16. //void CPLI_CalculateLength_OGG(CPs_PlaylistItem* pItem);
  17. void CPLI_CalculateLength_MP3(CPs_PlaylistItem* pItem);
  18. void CPLI_ShrinkFile(HANDLE hFile, const DWORD dwStartOffset, const unsigned int iNumBytes);
  19. BOOL CPLI_GrowFile(HANDLE hFile, const DWORD dwStartOffset, const unsigned int iNumBytes);
  20. ////////////////////////////////////////////////////////////////////////////////
  21. //
  22. //
  23. //
  24. CP_HPLAYLISTITEM CPLII_CreateItem(const char* pcPath)
  25. {
  26.     CPs_PlaylistItem* pNewItem = (CPs_PlaylistItem*)malloc(sizeof(CPs_PlaylistItem));
  27.     pNewItem->m_pcPath = NULL;
  28.     CPLI_SetPath(pNewItem, pcPath);
  29.     pNewItem->m_cTrackStackPos_AsText[0] = '';
  30.     pNewItem->m_iTrackStackPos = CIC_TRACKSTACK_UNSTACKED;
  31.     pNewItem->m_enTagType = ttUnread;
  32.     pNewItem->m_bID3Tag_SaveRequired = FALSE;
  33.     pNewItem->m_bDestroyOnDeactivate = FALSE;
  34.     pNewItem->m_pcArtist = NULL;
  35.     pNewItem->m_pcAlbum = NULL;
  36.     pNewItem->m_pcTrackName = NULL;
  37.     pNewItem->m_pcComment = NULL;
  38.     pNewItem->m_pcYear = NULL;
  39.     pNewItem->m_cGenre = CIC_INVALIDGENRE;
  40.     pNewItem->m_cTrackNum = CIC_INVALIDTRACKNUM;
  41.     pNewItem->m_pcTrackNum_AsText = NULL;
  42.     pNewItem->m_iTrackLength = 0;
  43.     pNewItem->m_pcTrackLength_AsText = NULL;
  44.     pNewItem->m_iCookie = -1;
  45.     pNewItem->m_hNext = NULL;
  46.     pNewItem->m_hPrev = NULL;
  47.     return pNewItem;
  48. }
  49. //
  50. //
  51. //
  52. void CPLII_RemoveTagInfo(CPs_PlaylistItem* pItem)
  53. {
  54.     if(pItem->m_pcArtist)
  55.     {
  56.         free(pItem->m_pcArtist);
  57.         pItem->m_pcArtist = NULL;
  58.     }
  59.     if(pItem->m_pcAlbum)
  60.     {
  61.         free(pItem->m_pcAlbum);
  62.         pItem->m_pcAlbum = NULL;
  63.     }
  64.     if(pItem->m_pcTrackName)
  65.     {
  66.         free(pItem->m_pcTrackName);
  67.         pItem->m_pcTrackName = NULL;
  68.     }
  69.     if(pItem->m_pcComment)
  70.     {
  71.         free(pItem->m_pcComment);
  72.         pItem->m_pcComment = NULL;
  73.     }
  74.     if(pItem->m_pcYear)
  75.     {
  76.         free(pItem->m_pcYear);
  77.         pItem->m_pcYear = NULL;
  78.     }
  79.     if(pItem->m_pcTrackNum_AsText)
  80.     {
  81.         free(pItem->m_pcTrackNum_AsText);
  82.         pItem->m_pcTrackNum_AsText = NULL;
  83.     }
  84.     if(pItem->m_pcTrackLength_AsText)
  85.     {
  86.         free(pItem->m_pcTrackLength_AsText);
  87.         pItem->m_pcTrackLength_AsText = NULL;
  88.     }
  89.     pItem->m_cGenre = CIC_INVALIDGENRE;
  90.     pItem->m_iTrackLength = 0;
  91.     pItem->m_cTrackNum = CIC_INVALIDTRACKNUM;
  92.     pItem->m_enTagType = ttUnread;
  93.     pItem->m_bID3Tag_SaveRequired = FALSE;
  94. }
  95. //
  96. //
  97. //
  98. void CPLII_DestroyItem(CP_HPLAYLISTITEM hItem)
  99. {
  100.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  101.     CP_CHECKOBJECT(pItem);
  102.     CPLII_RemoveTagInfo(pItem);
  103.     free(pItem->m_pcPath);
  104.     free(pItem);
  105. }
  106. //
  107. //
  108. //
  109. const char* CPLI_GetPath(const CP_HPLAYLISTITEM hItem)
  110. {
  111.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  112.     CP_CHECKOBJECT(pItem);
  113.     return pItem->m_pcPath;
  114. }
  115. //
  116. //
  117. //
  118. const char* CPLI_GetFilename(const CP_HPLAYLISTITEM hItem)
  119. {
  120.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  121.     CP_CHECKOBJECT(pItem);
  122.     return pItem->m_pcFilename;
  123. }
  124. //
  125. //
  126. //
  127. int CPLI_GetTrackStackPos(const CP_HPLAYLISTITEM hItem)
  128. {
  129.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  130.     CP_CHECKOBJECT(pItem);
  131.     return pItem->m_iTrackStackPos;
  132. }
  133. //
  134. //
  135. //
  136. const char* CPLI_GetTrackStackPos_AsText(const CP_HPLAYLISTITEM hItem)
  137. {
  138.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  139.     CP_CHECKOBJECT(pItem);
  140.     return pItem->m_cTrackStackPos_AsText;
  141. }
  142. //
  143. //
  144. //
  145. const char* CPLI_GetArtist(const CP_HPLAYLISTITEM hItem)
  146. {
  147.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  148.     CP_CHECKOBJECT(pItem);
  149.     return pItem->m_pcArtist;
  150. }
  151. //
  152. //
  153. //
  154. const char* CPLI_GetAlbum(const CP_HPLAYLISTITEM hItem)
  155. {
  156.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  157.     CP_CHECKOBJECT(pItem);
  158.     return pItem->m_pcAlbum;
  159. }
  160. //
  161. //
  162. //
  163. const char* CPLI_GetTrackName(const CP_HPLAYLISTITEM hItem)
  164. {
  165.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  166.     CP_CHECKOBJECT(pItem);
  167.     return pItem->m_pcTrackName;
  168. }
  169. //
  170. //
  171. //
  172. const char* CPLI_GetComment(const CP_HPLAYLISTITEM hItem)
  173. {
  174.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  175.     CP_CHECKOBJECT(pItem);
  176.     return pItem->m_pcComment;
  177. }
  178. //
  179. //
  180. //
  181. const char* CPLI_GetYear(const CP_HPLAYLISTITEM hItem)
  182. {
  183.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  184.     CP_CHECKOBJECT(pItem);
  185.     return pItem->m_pcYear;
  186. }
  187. //
  188. //
  189. //
  190. const char* CPLI_GetGenre(const CP_HPLAYLISTITEM hItem)
  191. {
  192.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  193.     CP_CHECKOBJECT(pItem);
  194.     if(pItem->m_cGenre == CIC_INVALIDGENRE)
  195.         return NULL;
  196.     return glb_pcGenres[pItem->m_cGenre];
  197. }
  198. //
  199. //
  200. //
  201. const unsigned char CPLI_GetTrackNum(const CP_HPLAYLISTITEM hItem)
  202. {
  203.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  204.     CP_CHECKOBJECT(pItem);
  205.     return pItem->m_cTrackNum;
  206. }
  207. //
  208. //
  209. //
  210. const char* CPLI_GetTrackNum_AsText(const CP_HPLAYLISTITEM hItem)
  211. {
  212.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  213.     CP_CHECKOBJECT(pItem);
  214.     return pItem->m_pcTrackNum_AsText;
  215. }
  216. //
  217. //
  218. //
  219. const char* CPLI_GetTrackLength_AsText(const CP_HPLAYLISTITEM hItem)
  220. {
  221.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  222.     CP_CHECKOBJECT(pItem);
  223.     return pItem->m_pcTrackLength_AsText;
  224. }
  225. //
  226. //
  227. //
  228. int CPLI_GetTrackLength(const CP_HPLAYLISTITEM hItem)
  229. {
  230.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  231.     CP_CHECKOBJECT(pItem);
  232.     return pItem->m_iTrackLength;
  233. }
  234. //
  235. //
  236. //
  237. void CPLI_SetCookie(CP_HPLAYLISTITEM hItem, const int iCookie)
  238. {
  239.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  240.     CP_CHECKOBJECT(pItem);
  241.     pItem->m_iCookie = iCookie;
  242. }
  243. //
  244. //
  245. //
  246. int CPLI_GetCookie(const CP_HPLAYLISTITEM hItem)
  247. {
  248.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  249.     CP_CHECKOBJECT(pItem);
  250.     return pItem->m_iCookie;
  251. }
  252. //
  253. //
  254. //
  255. CP_HPLAYLISTITEM CPLI_Next(const CP_HPLAYLISTITEM hItem)
  256. {
  257.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  258.     CP_CHECKOBJECT(pItem);
  259.     return pItem->m_hNext;
  260. }
  261. //
  262. //
  263. //
  264. CP_HPLAYLISTITEM CPLI_Prev(const CP_HPLAYLISTITEM hItem)
  265. {
  266.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  267.     CP_CHECKOBJECT(pItem);
  268.     return pItem->m_hPrev;
  269. }
  270. //
  271. //
  272. //
  273. char* DecodeID3String(const char* pcSource, const int iLength)
  274. {
  275.     char* cWorkString = (char*)_alloca(iLength+1);
  276.     char* pDestString;
  277.     char* pcLastWhiteSpace;
  278.     int iCharIDX;
  279.     cWorkString[iLength] = '';
  280.     memcpy(cWorkString, pcSource, iLength);
  281.     // Remove trailing whitespace
  282.     pcLastWhiteSpace = NULL;
  283.     for(iCharIDX = 0; cWorkString[iCharIDX]; iCharIDX++)
  284.     {
  285.         if(cWorkString[iCharIDX] == ' ')
  286.         {
  287.             if(!pcLastWhiteSpace)
  288.                 pcLastWhiteSpace = cWorkString + iCharIDX;
  289.         }
  290.         else
  291.             pcLastWhiteSpace = NULL;
  292.     }
  293.     if(pcLastWhiteSpace)
  294.         *pcLastWhiteSpace = '';
  295.     // Copy string
  296.     STR_AllocSetString(&pDestString, cWorkString, FALSE);
  297.     return pDestString;
  298. }
  299. //
  300. //
  301. //
  302. void CPLI_ReadTag(CP_HPLAYLISTITEM hItem)
  303. {
  304.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  305.     HANDLE hFile;
  306.     CP_CHECKOBJECT(pItem);
  307.     if(pItem->m_enTagType != ttUnread)
  308.         return;
  309.     // - Try to open the file
  310.     hFile = CreateFile(pItem->m_pcPath, GENERIC_READ,
  311.                        FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
  312.                        OPEN_EXISTING, 0, 0);
  313.     // Cannot open - fail silently
  314.     if(hFile == INVALID_HANDLE_VALUE)
  315.         return;
  316.     // Try to read a V2 tag
  317.     if(options.support_id3v2)
  318.         CPLI_ReadTag_ID3v2(pItem, hFile);
  319.     // Failed? - try a V1 tag instead
  320.     if(pItem->m_enTagType == ttUnread)
  321.         CPLI_ReadTag_ID3v1(pItem, hFile);
  322.     CloseHandle(hFile);
  323.     // Update interface
  324.     CPL_cb_OnItemUpdated(hItem);
  325. }
  326. //
  327. //
  328. //
  329. char* CPLI_ID3v2_DecodeString(const BYTE* pSourceText, const int iTagDataSize)
  330. {
  331.     int iStringLength;
  332.     char* pcDestString;
  333.     if(pSourceText[0] == '')
  334.     {
  335.         iStringLength = iTagDataSize-1;
  336.         pcDestString = malloc(iStringLength+1);
  337.         memcpy(pcDestString, pSourceText+1, iStringLength);
  338.         pcDestString[iStringLength] = 0;
  339.     }
  340.     else
  341.     {
  342.         CP_TRACE0("ID3v2 Unknown encoding");
  343.         pcDestString = NULL;
  344.     }
  345.     return pcDestString;
  346. }
  347. //
  348. //
  349. //
  350. void CPLI_DecodeLength(CPs_PlaylistItem* pItem, unsigned int iNewLength)
  351. {
  352.     int iHours, iMins, iSecs;
  353.     // Free existing buffer
  354.     if(pItem->m_pcTrackLength_AsText)
  355.     {
  356.         free(pItem->m_pcTrackLength_AsText);
  357.         pItem->m_pcTrackLength_AsText = NULL;
  358.     }
  359.     pItem->m_iTrackLength = iNewLength;
  360.     iHours = iNewLength/3600;
  361.     iMins = (iNewLength-(iHours*3600)) / 60;
  362.     iSecs = iNewLength - (iHours*3600) - (iMins*60);
  363.     // If length has hours then format as hh:mm:ss otherwise format as mm:ss
  364.     if(iHours > 0)
  365.     {
  366.         pItem->m_pcTrackLength_AsText = (char*)malloc(9);
  367.         sprintf(pItem->m_pcTrackLength_AsText, "%02d:%02d:%02d", iHours, iMins, iSecs);
  368.     }
  369.     else
  370.     {
  371.         pItem->m_pcTrackLength_AsText = (char*)malloc(6);
  372.         sprintf(pItem->m_pcTrackLength_AsText, "%02d:%02d", iMins, iSecs);
  373.     }
  374. }
  375. //
  376. //
  377. //
  378. void CPLI_ReadTag_ID3v2(CPs_PlaylistItem* pItem, HANDLE hFile)
  379. {
  380.     DWORD dwBytesRead;
  381.     int iTagDataToRead;
  382.     CIs_ID3v2Tag ID3v2;
  383.     SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
  384.     ReadFile(hFile, &ID3v2, sizeof(ID3v2), &dwBytesRead, NULL);
  385.     // Not enough file data returned - or the data returned does not look like an ID3
  386.     if(dwBytesRead != sizeof(ID3v2)
  387.             || memcmp(ID3v2.m_cTAG, "ID3", 3) != 0
  388.             || (ID3v2.m_cVersion[0] != 0x03 && ID3v2.m_cVersion[0] != 0x04)) // Major version wrong
  389.     {
  390.         return;
  391.     }
  392.     // Work out the amount of tag left to read
  393.     iTagDataToRead = (ID3v2.m_cSize_Encoded[0] << 21)
  394.                      | (ID3v2.m_cSize_Encoded[1] << 14)
  395.                      | (ID3v2.m_cSize_Encoded[2] << 7)
  396.                      | ID3v2.m_cSize_Encoded[3];
  397.     // Check for a big enough file now (to save endless checking)
  398.     if(GetFileSize(hFile, NULL) < (sizeof(ID3v2) + iTagDataToRead))
  399.         return;
  400.     // Skip over extended header (if there is one)
  401.     if(ID3v2.m_cFlags & ID3v2_FLAG_EXTENDEDHEADER)
  402.     {
  403.         char cExtendedHeaderSize_Encoded[4];
  404.         int iExtendedHeaderSize;
  405.         ReadFile(hFile, cExtendedHeaderSize_Encoded, sizeof(cExtendedHeaderSize_Encoded), &dwBytesRead, NULL);
  406.         iExtendedHeaderSize = (cExtendedHeaderSize_Encoded[0] << 21)
  407.                               | (cExtendedHeaderSize_Encoded[1] << 14)
  408.                               | (cExtendedHeaderSize_Encoded[2] << 7)
  409.                               | cExtendedHeaderSize_Encoded[3];
  410.         SetFilePointer(hFile, iExtendedHeaderSize - sizeof(cExtendedHeaderSize_Encoded), NULL, FILE_CURRENT);
  411.         iTagDataToRead -= iExtendedHeaderSize;
  412.     }
  413.     while(iTagDataToRead > sizeof(CIs_ID3v2Frame))
  414.     {
  415.         CIs_ID3v2Frame ID3v2Frame;
  416.         BYTE* pFrameData;
  417.         int iFrameSize;
  418.         ReadFile(hFile, &ID3v2Frame, sizeof(ID3v2Frame), &dwBytesRead, NULL);
  419.         // Have we encountered padding?
  420.         if(ID3v2Frame.m_cFrameID[0] == '')
  421.             break;
  422.         if(ID3v2.m_cVersion[0] == 0x03)
  423.         {
  424.             iFrameSize = (ID3v2Frame.m_cSize_Encoded[0] << 24)
  425.                          | (ID3v2Frame.m_cSize_Encoded[1] << 16)
  426.                          | (ID3v2Frame.m_cSize_Encoded[2] << 8)
  427.                          | ID3v2Frame.m_cSize_Encoded[3];
  428.         }
  429.         else
  430.         {
  431.             iFrameSize = (ID3v2Frame.m_cSize_Encoded[0] << 21)
  432.                          | (ID3v2Frame.m_cSize_Encoded[1] << 14)
  433.                          | (ID3v2Frame.m_cSize_Encoded[2] << 7)
  434.                          | ID3v2Frame.m_cSize_Encoded[3];
  435.         }
  436.         // Frame size invalid?
  437.         if(iFrameSize > iTagDataToRead)
  438.             return;
  439.         pFrameData = malloc(iFrameSize+1);
  440.         if(!ReadFile(hFile, pFrameData, iFrameSize, &dwBytesRead, NULL)) return;
  441.         pFrameData[iFrameSize] = '';
  442.         // Decode frames
  443.         if(memcmp(ID3v2Frame.m_cFrameID, "TIT2", 4) == 0)
  444.             pItem->m_pcTrackName = CPLI_ID3v2_DecodeString(pFrameData, iFrameSize);
  445.         else if(memcmp(ID3v2Frame.m_cFrameID, "TPE1", 4) == 0)
  446.             pItem->m_pcArtist = CPLI_ID3v2_DecodeString(pFrameData, iFrameSize);
  447.         else if(memcmp(ID3v2Frame.m_cFrameID, "TALB", 4) == 0)
  448.             pItem->m_pcAlbum = CPLI_ID3v2_DecodeString(pFrameData, iFrameSize);
  449.         else if(memcmp(ID3v2Frame.m_cFrameID, "TRCK", 4) == 0)
  450.         {
  451.             pItem->m_pcTrackNum_AsText = CPLI_ID3v2_DecodeString(pFrameData, iFrameSize);
  452.             if(pItem->m_pcTrackNum_AsText)
  453.                 pItem->m_cTrackNum = (unsigned char)atoi(pItem->m_pcTrackNum_AsText);
  454.         }
  455.         else if(memcmp(ID3v2Frame.m_cFrameID, "TYER", 4) == 0)
  456.             pItem->m_pcYear = CPLI_ID3v2_DecodeString(pFrameData, iFrameSize);
  457.         else if(memcmp(ID3v2Frame.m_cFrameID, "TENC", 4) == 0)
  458.             pItem->m_pcComment = CPLI_ID3v2_DecodeString(pFrameData, iFrameSize);
  459.         else if(memcmp(ID3v2Frame.m_cFrameID, "TCON", 4) == 0)
  460.         {
  461.             char* pcGenre = CPLI_ID3v2_DecodeString(pFrameData, iFrameSize);
  462.             if(pcGenre)
  463.             {
  464.                 // Search for this genre among the ID3v1 genres (don't read it if we cannot find it)
  465.                 int iGenreIDX;
  466.                 for(iGenreIDX = 0; iGenreIDX < CIC_NUMGENRES; iGenreIDX++)
  467.                 {
  468.                     if(stricmp(pcGenre, glb_pcGenres[iGenreIDX]) == 0)
  469.                     {
  470.                         pItem->m_cGenre = (unsigned char)iGenreIDX;
  471.                         break;
  472.                     }
  473.                 }
  474.                 free(pcGenre);
  475.             }
  476.         }
  477.         else if(memcmp(ID3v2Frame.m_cFrameID, "TLEN", 4) == 0)
  478.         {
  479.             char* pcLength = CPLI_ID3v2_DecodeString(pFrameData, iFrameSize);
  480.             if(pcLength)
  481.             {
  482.                 CPLI_DecodeLength(pItem, atoi(pcLength)/1000);
  483.                 free(pcLength);
  484.             }
  485.         }
  486. #ifdef _DEBUG
  487.         /*
  488.         else if(ID3v2Frame.m_cFrameID[0] == 'T')
  489.          CP_TRACE2("Text frame %4s "%s"", ID3v2Frame.m_cFrameID, pFrameData+1);
  490.         else
  491.          CP_TRACE1("Any old frame %4s", ID3v2Frame.m_cFrameID);
  492.         */
  493. #endif
  494.         free(pFrameData);
  495.         iTagDataToRead -= iFrameSize + sizeof(ID3v2Frame);
  496.     }
  497.     pItem->m_enTagType = ttID3v2;
  498. }
  499. //
  500. //
  501. //
  502. void CPLI_ReadTag_ID3v1(CPs_PlaylistItem* pItem, HANDLE hFile)
  503. {
  504.     DWORD dwBytesRead;
  505.     CIs_ID3Tag ID3;
  506.     SetFilePointer(hFile, 0-sizeof(ID3), NULL, FILE_END);
  507.     ReadFile(hFile, &ID3, sizeof(ID3), &dwBytesRead, NULL);
  508.     // Not enough file data returned - or the data returned does not look like an ID3
  509.     if(dwBytesRead != sizeof(ID3) || memcmp(ID3.m_cTAG, "TAG", 3) != 0)
  510.         return;
  511.     // Decode the fixed strings into our dynamic strings
  512.     CPLII_RemoveTagInfo(pItem);
  513.     pItem->m_pcTrackName = DecodeID3String(ID3.m_cSong, 30);
  514.     pItem->m_pcArtist = DecodeID3String(ID3.m_cArtist, 30);
  515.     pItem->m_pcAlbum = DecodeID3String(ID3.m_cAlbum, 30);
  516.     pItem->m_pcYear = DecodeID3String(ID3.m_cYear, 4);
  517.     // ID3v1.1 - If the 29th byte of the comment is 0 then the 30th byte is the track num
  518.     // ** Some dodgy implementations of ID3v1.1 just slap a <32 char byte at position 30 and hope
  519.     //    for the best - handle these too <hmph!>
  520.     if(ID3.m_cComment[28] == '' || ID3.m_cComment[29] < 32)
  521.     {
  522.         char cTempString[33];
  523.         pItem->m_pcComment = DecodeID3String(ID3.m_cComment, 28);
  524.         pItem->m_cTrackNum = ID3.m_cComment[29];
  525.         if(pItem->m_cTrackNum != CIC_INVALIDTRACKNUM)
  526.         {
  527.             itoa(pItem->m_cTrackNum, cTempString, 10);
  528.             pItem->m_pcTrackNum_AsText = (char*)malloc(CPC_TRACKNUMASTEXTBUFFERSIZE);
  529.             strncpy(pItem->m_pcTrackNum_AsText, cTempString, CPC_TRACKNUMASTEXTBUFFERSIZE);
  530.         }
  531.     }
  532.     else
  533.     {
  534.         pItem->m_pcComment = DecodeID3String(ID3.m_cComment, 30);
  535.         pItem->m_cTrackNum = CIC_INVALIDTRACKNUM;
  536.     }
  537.     if(ID3.m_cGenre < CIC_NUMGENRES)
  538.         pItem->m_cGenre = ID3.m_cGenre;
  539.     pItem->m_enTagType = ttID3v1;
  540. }
  541. //
  542. //
  543. //
  544. BOOL CPLI_IsTagDirty(CP_HPLAYLISTITEM hItem)
  545. {
  546.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  547.     CP_CHECKOBJECT(pItem);
  548.     return pItem->m_bID3Tag_SaveRequired;
  549. }
  550. //
  551. //
  552. //
  553. void CPLI_WriteTag(CP_HPLAYLISTITEM hItem)
  554. {
  555.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  556.     HANDLE hFile;
  557.     CP_CHECKOBJECT(pItem);
  558.     if(pItem->m_bID3Tag_SaveRequired == FALSE)
  559.         return;
  560. if(stricmp(".ogg", CPLI_GetExtension(hItem)) != 0 && 
  561. stricmp(".mp3", CPLI_GetExtension(hItem)) !=0)
  562. return;
  563.     // Try to open the file
  564.     hFile = CreateFile(pItem->m_pcPath, GENERIC_READ | GENERIC_WRITE,
  565.                        FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
  566.                        OPEN_EXISTING, 0, 0);
  567.     // Cannot open - fail silently
  568.     if(hFile == INVALID_HANDLE_VALUE)
  569.         return;
  570.     pItem->m_bID3Tag_SaveRequired = FALSE;
  571. if (stricmp(".mp3", CPLI_GetExtension(hItem)) ==0)
  572.     {
  573.         CPLI_WriteTag_ID3v1(pItem, hFile);
  574.         if(options.support_id3v2)
  575.             CPLI_WriteTag_ID3v2(pItem, hFile);
  576.     }
  577.     CloseHandle(hFile);
  578. }
  579. //
  580. //
  581. //
  582. void CPLI_WriteTag_ID3v1(CPs_PlaylistItem* pItem, HANDLE hFile)
  583. {
  584.     DWORD dwBytesTransferred;
  585.     CIs_ID3Tag ID3;
  586.     char cTagMagic[3];
  587.     // Build the tag (of ID3v1.1 format)
  588.     memset(&ID3, 32, sizeof(ID3));
  589.     memcpy(ID3.m_cTAG, "TAG", 3);
  590.     if(pItem->m_pcTrackName)
  591.         strncpy(ID3.m_cSong, pItem->m_pcTrackName, 30);
  592.     if(pItem->m_pcArtist)
  593.         strncpy(ID3.m_cArtist, pItem->m_pcArtist, 30);
  594.     if(pItem->m_pcAlbum)
  595.         strncpy(ID3.m_cAlbum, pItem->m_pcAlbum, 30);
  596.     if(pItem->m_pcYear)
  597.         strncpy(ID3.m_cYear, pItem->m_pcYear, 4);
  598.     if(pItem->m_pcComment)
  599. if(strlen(pItem->m_pcComment)>28)
  600. {
  601.         strncpy(ID3.m_cComment, pItem->m_pcComment, 30);
  602. }else
  603. {
  604.         strncpy(ID3.m_cComment, pItem->m_pcComment, 28);
  605.     ID3.m_cComment[28] = '';
  606.     ID3.m_cComment[29] = pItem->m_cTrackNum;
  607. }
  608.     ID3.m_cGenre = pItem->m_cGenre;
  609.     // Set the file pointer to the end of the file (or the start of the tag if there is one already)
  610.     SetFilePointer(hFile, 0-sizeof(ID3), NULL, FILE_END);
  611.     ReadFile(hFile, cTagMagic, sizeof(cTagMagic), &dwBytesTransferred, NULL);
  612.     if(memcmp(cTagMagic, "TAG", 3) == 0)
  613.         SetFilePointer(hFile, 0-sizeof(ID3), NULL, FILE_END);
  614.     else
  615.         SetFilePointer(hFile, 0, NULL, FILE_END);
  616.     WriteFile(hFile, &ID3, sizeof(ID3), &dwBytesTransferred, NULL);
  617. }
  618. //
  619. //
  620. //
  621. void CPLI_ID3v2_WriteSyncSafeInt(char cDest[4], const int iSource)
  622. {
  623.     cDest[0] = (iSource>>21) & 0x7F;
  624.     cDest[1] = (iSource>>14) & 0x7F;
  625.     cDest[2] = (iSource>>7) & 0x7F;
  626.     cDest[3] = iSource & 0x7F;
  627. }
  628. //
  629. //
  630. //
  631. void CPLI_ID3v2_WriteTextFrame(BYTE** ppDest, const char pcTag[4], const char* pcString)
  632. {
  633.     CIs_ID3v2Frame* pFrame = (CIs_ID3v2Frame*)*ppDest;
  634.     BYTE* pFrameData;
  635.     int iFrameDataLength;
  636.     iFrameDataLength = strlen(pcString) + 1; // 1-byte for encoding
  637.     memcpy(pFrame->m_cFrameID, pcTag, sizeof(pcTag));
  638.     CPLI_ID3v2_WriteSyncSafeInt(pFrame->m_cSize_Encoded, iFrameDataLength);
  639.     pFrame->m_cFlags = 0x0;
  640.     // Write frame data
  641.     pFrameData = ((*ppDest) + sizeof(CIs_ID3v2Frame));
  642.     pFrameData[0] = 0x0;
  643.     memcpy(pFrameData + 1, pcString, iFrameDataLength-1);
  644.     *ppDest += iFrameDataLength + sizeof(CIs_ID3v2Frame);
  645. }
  646. //
  647. //
  648. //
  649. void CPLI_WriteTag_ID3v2(CPs_PlaylistItem* pItem, HANDLE hFile)
  650. {
  651.     unsigned int iTagDataLength;
  652.     unsigned int iExistingTagLength;
  653.     DWORD dwBytesTransferred;
  654.     char atiobuffer[33];
  655.     BYTE* pTag;
  656.     BYTE* pTag_Cursor;
  657.     // Work out the size of the data in the tag frames
  658.     iTagDataLength = 0;
  659.     if(pItem->m_pcTrackName)
  660.         iTagDataLength += strlen(pItem->m_pcTrackName) + 1 + sizeof(CIs_ID3v2Frame); // 1-byte for encoding and a frame header
  661.     if(pItem->m_pcArtist)
  662.         iTagDataLength += strlen(pItem->m_pcArtist) + 1 + sizeof(CIs_ID3v2Frame); // 1-byte for encoding and a frame header
  663.     if(pItem->m_pcAlbum)
  664.         iTagDataLength += strlen(pItem->m_pcAlbum) + 1 + sizeof(CIs_ID3v2Frame); // 1-byte for encoding and a frame header
  665.     if(pItem->m_pcYear)
  666.         iTagDataLength += strlen(pItem->m_pcYear) + 1 + sizeof(CIs_ID3v2Frame); // 1-byte for encoding and a frame header
  667.     if(pItem->m_pcComment)
  668.         iTagDataLength += strlen(pItem->m_pcComment) + 1 + sizeof(CIs_ID3v2Frame); // 1-byte for encoding and a frame header
  669.     if(pItem->m_cGenre != CIC_INVALIDGENRE)
  670.         iTagDataLength += strlen(glb_pcGenres[pItem->m_cGenre]) + 1 + sizeof(CIs_ID3v2Frame); // 1-byte for encoding and a frame header
  671.     if(pItem->m_cTrackNum != CIC_INVALIDTRACKNUM)
  672.         iTagDataLength += strlen(itoa(pItem->m_cTrackNum, atiobuffer, 10)) + 1 + sizeof(CIs_ID3v2Frame); // 1-byte for encoding and a frame header
  673.     if(pItem->m_iTrackLength != 0)
  674.         iTagDataLength += strlen(itoa(pItem->m_iTrackLength*1000, atiobuffer, 10)) + 1 + sizeof(CIs_ID3v2Frame); // 1-byte for encoding and a frame header
  675.     // Add ID3v2 overhead
  676.     iTagDataLength += sizeof(CIs_ID3v2Tag);
  677.     // Quantise tag to the nearest 1K
  678.     iTagDataLength = ((iTagDataLength>>10) + 1) << 10;
  679.     // Is there a tag big enough in the file
  680.     {
  681.         CIs_ID3v2Tag existingtagheader;
  682.         SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
  683.         ReadFile(hFile, &existingtagheader, sizeof(existingtagheader), &dwBytesTransferred, NULL);
  684.         if(memcmp(existingtagheader.m_cTAG, "ID3", 3) == 0)
  685.         {
  686.             iExistingTagLength = (existingtagheader.m_cSize_Encoded[0] << 21)
  687.                                  | (existingtagheader.m_cSize_Encoded[1] << 14)
  688.                                  | (existingtagheader.m_cSize_Encoded[2] << 7)
  689.                                  | existingtagheader.m_cSize_Encoded[3];
  690.             iExistingTagLength += sizeof(CIs_ID3v2Tag); // count the header
  691.             if(iExistingTagLength > iTagDataLength)
  692.                 iTagDataLength = iExistingTagLength;
  693.         }
  694.         else
  695.             iExistingTagLength = 0;
  696.     }
  697.     // Do we need to enlarge the file?
  698.     if(iExistingTagLength < iTagDataLength)
  699.     {
  700.         if(CPLI_GrowFile(hFile, 0, iTagDataLength-iExistingTagLength) == FALSE)
  701.             return;
  702.     }
  703.     // Build tag
  704.     pTag = malloc(iTagDataLength);
  705.     memset(pTag, 0, iTagDataLength); // ** must do this as all padding should be 0x00
  706.     pTag_Cursor = pTag;
  707.     // Header
  708.     {
  709.         CIs_ID3v2Tag* pHeader = (CIs_ID3v2Tag*)pTag_Cursor;
  710.         int iSizeLessHeader = iTagDataLength - sizeof(CIs_ID3v2Tag);
  711.         pHeader->m_cTAG[0] = 'I';
  712.         pHeader->m_cTAG[1] = 'D';
  713.         pHeader->m_cTAG[2] = '3';
  714.         pHeader->m_cVersion[0] = 0x4;
  715.         pHeader->m_cVersion[1] = 0x0;
  716.         pHeader->m_cFlags = 0x0;
  717.         CPLI_ID3v2_WriteSyncSafeInt(pHeader->m_cSize_Encoded, iSizeLessHeader);
  718.         pTag_Cursor += sizeof(CIs_ID3v2Tag);
  719.     }
  720.     // Frames
  721.     if(pItem->m_pcTrackName)
  722.         CPLI_ID3v2_WriteTextFrame(&pTag_Cursor, "TIT2", pItem->m_pcTrackName);
  723.     if(pItem->m_pcArtist)
  724.         CPLI_ID3v2_WriteTextFrame(&pTag_Cursor, "TPE1", pItem->m_pcArtist);
  725.     if(pItem->m_pcAlbum)
  726.         CPLI_ID3v2_WriteTextFrame(&pTag_Cursor, "TALB", pItem->m_pcAlbum);
  727.     if(pItem->m_pcYear)
  728.         CPLI_ID3v2_WriteTextFrame(&pTag_Cursor, "TYER", pItem->m_pcYear);
  729.     if(pItem->m_pcComment)
  730.         CPLI_ID3v2_WriteTextFrame(&pTag_Cursor, "TENC", pItem->m_pcComment);
  731.     if(pItem->m_cGenre != CIC_INVALIDGENRE)
  732.         CPLI_ID3v2_WriteTextFrame(&pTag_Cursor, "TCON", glb_pcGenres[pItem->m_cGenre]);
  733.     if(pItem->m_cTrackNum != CIC_INVALIDTRACKNUM)
  734.         CPLI_ID3v2_WriteTextFrame(&pTag_Cursor, "TRCK", itoa(pItem->m_cTrackNum, atiobuffer, 10));
  735.     if(pItem->m_iTrackLength != 0)
  736.         CPLI_ID3v2_WriteTextFrame(&pTag_Cursor, "TLEN", itoa(pItem->m_iTrackLength * 1000, atiobuffer, 10));
  737.     // Output tag
  738.     SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
  739.     WriteFile(hFile, pTag, iTagDataLength, &dwBytesTransferred, NULL);
  740.     CP_ASSERT(dwBytesTransferred == iTagDataLength);
  741. }
  742. //
  743. //
  744. //
  745. CPe_ReadWriteState CPLI_GetReadWriteState(const CP_HPLAYLISTITEM hItem)
  746. {
  747.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  748.     HANDLE hFile;
  749.     CP_CHECKOBJECT(pItem);
  750.     // We will check this every time (and not cache the result) because the
  751.     // file could have been played with outside of CoolPlayer
  752.     // Try to open the file in RW mode
  753.     hFile = CreateFile(pItem->m_pcPath, GENERIC_READ | GENERIC_WRITE,
  754.                        FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
  755.                        OPEN_EXISTING, 0, 0);
  756.     if(hFile != INVALID_HANDLE_VALUE)
  757.     {
  758.         // Only cache
  759.         CloseHandle(hFile);
  760.         return rwsReadWrite;
  761.     }
  762.     // That didn't work - try a RO open
  763.     hFile = CreateFile(pItem->m_pcPath, GENERIC_READ,
  764.                        FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
  765.                        OPEN_EXISTING, 0, 0);
  766.     if(hFile != INVALID_HANDLE_VALUE)
  767.     {
  768.         CloseHandle(hFile);
  769.         return rwsReadOnly;
  770.     }
  771.     return rwsBadFile;
  772. }
  773. //
  774. //
  775. //
  776. void CPLI_SetArtist(CP_HPLAYLISTITEM hItem, const char* pcNewValue)
  777. {
  778.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  779.     CP_CHECKOBJECT(pItem);
  780.     STR_AllocSetString(&pItem->m_pcArtist, pcNewValue, TRUE);
  781.     pItem->m_bID3Tag_SaveRequired = TRUE;
  782.     CPL_cb_OnItemUpdated(hItem);
  783. }
  784. //
  785. //
  786. //
  787. void CPLI_SetAlbum(CP_HPLAYLISTITEM hItem, const char* pcNewValue)
  788. {
  789.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  790.     CP_CHECKOBJECT(pItem);
  791.     STR_AllocSetString(&pItem->m_pcAlbum, pcNewValue, TRUE);
  792.     pItem->m_bID3Tag_SaveRequired = TRUE;
  793.     CPL_cb_OnItemUpdated(hItem);
  794. }
  795. //
  796. //
  797. //
  798. void CPLI_SetTrackName(CP_HPLAYLISTITEM hItem, const char* pcNewValue)
  799. {
  800.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  801.     CP_CHECKOBJECT(pItem);
  802.     STR_AllocSetString(&pItem->m_pcTrackName, pcNewValue, TRUE);
  803.     pItem->m_bID3Tag_SaveRequired = TRUE;
  804.     CPL_cb_OnItemUpdated(hItem);
  805. }
  806. //
  807. //
  808. //
  809. void CPLI_SetYear(CP_HPLAYLISTITEM hItem, const char* pcNewValue)
  810. {
  811.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  812.     CP_CHECKOBJECT(pItem);
  813.     STR_AllocSetString(&pItem->m_pcYear, pcNewValue, TRUE);
  814.     pItem->m_bID3Tag_SaveRequired = TRUE;
  815.     CPL_cb_OnItemUpdated(hItem);
  816. }
  817. //
  818. //
  819. //
  820. void CPLI_SetGenreIDX(CP_HPLAYLISTITEM hItem, const unsigned char iNewValue)
  821. {
  822.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  823.     CP_CHECKOBJECT(pItem);
  824.     pItem->m_cGenre = iNewValue;
  825.     pItem->m_bID3Tag_SaveRequired = TRUE;
  826.     CPL_cb_OnItemUpdated(hItem);
  827. }
  828. //
  829. //
  830. //
  831. void CPLI_SetTrackNum(CP_HPLAYLISTITEM hItem, const unsigned char iNewValue)
  832. {
  833.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  834.     char cTempString[33];
  835.     CP_CHECKOBJECT(pItem);
  836.     pItem->m_cTrackNum = iNewValue;
  837.     if(pItem->m_cTrackNum != CIC_INVALIDTRACKNUM)
  838.     {
  839.         if(pItem->m_pcTrackNum_AsText)
  840.             free(pItem->m_pcTrackNum_AsText);
  841.         pItem->m_pcTrackNum_AsText = (char*)malloc(CPC_TRACKNUMASTEXTBUFFERSIZE);
  842.         itoa(pItem->m_cTrackNum, cTempString, 10);
  843.         strncpy(pItem->m_pcTrackNum_AsText, cTempString, CPC_TRACKNUMASTEXTBUFFERSIZE);
  844.     }
  845.     else
  846.     {
  847.         if(pItem->m_pcTrackNum_AsText)
  848.         {
  849.             free(pItem->m_pcTrackNum_AsText);
  850.             pItem->m_pcTrackNum_AsText = NULL;
  851.         }
  852.     }
  853.     pItem->m_bID3Tag_SaveRequired = TRUE;
  854.     CPL_cb_OnItemUpdated(hItem);
  855. }
  856. //
  857. //
  858. //
  859. void CPLI_SetTrackNum_AsText(CP_HPLAYLISTITEM hItem, const char* pcNewValue)
  860. {
  861.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  862.     CP_CHECKOBJECT(pItem);
  863.     if(pcNewValue[0] == '')
  864.         pItem->m_cTrackNum = CIC_INVALIDTRACKNUM;
  865.     else
  866.         pItem->m_cTrackNum = (unsigned char)atoi(pcNewValue);
  867.     if(pItem->m_pcTrackNum_AsText)
  868.         free(pItem->m_pcTrackNum_AsText);
  869.     pItem->m_pcTrackNum_AsText = (char*)malloc(CPC_TRACKNUMASTEXTBUFFERSIZE);
  870.     strncpy(pItem->m_pcTrackNum_AsText, pcNewValue, CPC_TRACKNUMASTEXTBUFFERSIZE);
  871.     pItem->m_bID3Tag_SaveRequired = TRUE;
  872.     CPL_cb_OnItemUpdated(hItem);
  873. }
  874. //
  875. //
  876. //
  877. void CPLI_SetComment(CP_HPLAYLISTITEM hItem, const char* pcNewValue)
  878. {
  879.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  880.     CP_CHECKOBJECT(pItem);
  881.     STR_AllocSetString(&pItem->m_pcComment, pcNewValue, TRUE);
  882.     pItem->m_bID3Tag_SaveRequired = TRUE;
  883.     CPL_cb_OnItemUpdated(hItem);
  884. }
  885. //
  886. //
  887. //
  888. void CPLI_SetTrackStackPos(CP_HPLAYLISTITEM hItem, const int iNewPos)
  889. {
  890.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  891.     CP_CHECKOBJECT(pItem);
  892.     pItem->m_iTrackStackPos = iNewPos;
  893.     if(iNewPos == 0)
  894.     {
  895.         pItem->m_cTrackStackPos_AsText[0] = '>';
  896.         pItem->m_cTrackStackPos_AsText[1] = '>';
  897.         pItem->m_cTrackStackPos_AsText[2] = '>';
  898.         pItem->m_cTrackStackPos_AsText[3] = '';
  899.     }
  900.     else if(iNewPos == CIC_TRACKSTACK_UNSTACKED)
  901.     {
  902.         pItem->m_cTrackStackPos_AsText[0] = '';
  903.     }
  904.     else
  905.     {
  906.         _snprintf(pItem->m_cTrackStackPos_AsText, sizeof(pItem->m_cTrackStackPos_AsText), "%d", iNewPos);
  907.     }
  908. }
  909. //
  910. //
  911. //
  912. void CPLI_CalculateLength(CP_HPLAYLISTITEM hItem)
  913. {
  914.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  915.     const char* pcExtension;
  916.     CP_CHECKOBJECT(pItem);
  917.     pcExtension = CPLI_GetExtension(hItem);
  918.     if(stricmp(pcExtension, ".mp3") == 0
  919.             || stricmp(pcExtension, ".mp2") == 0)
  920.     {
  921.         CPLI_CalculateLength_MP3(pItem);
  922.     }
  923. //    pItem->m_bID3Tag_SaveRequired = TRUE;
  924.     CPL_cb_OnItemUpdated(hItem);
  925. }
  926. //
  927. //
  928. //
  929. //
  930. //
  931. //
  932. //
  933. //
  934. //
  935. void CPLI_CalculateLength_MP3(CPs_PlaylistItem* pItem)
  936. {
  937.     BYTE pbBuffer[0x8000];
  938.     unsigned int iBufferCursor;
  939.     DWORD dwBufferSize;
  940.     HANDLE hFile;
  941.     BOOL bFoundFrameHeader;
  942.     int iBitRate;
  943.     DWORD dwFileSize;
  944.     int iMPEG_version;
  945.     int iLayer;
  946.     BOOL bMono;
  947.     unsigned int iVBRHeader;
  948.     // - Try to open the file
  949.     hFile = CreateFile(pItem->m_pcPath, GENERIC_READ,
  950.                        FILE_SHARE_READ, 0,
  951.                        OPEN_EXISTING, 0, 0);
  952.     dwFileSize = GetFileSize(hFile, NULL);
  953.     // Cannot open - fail silently
  954.     if(hFile == INVALID_HANDLE_VALUE)
  955.         return;
  956.     // Read the first 64K of the file (that should contain the first frame header!)
  957.     ReadFile(hFile, pbBuffer, sizeof(pbBuffer), &dwBufferSize, NULL);
  958.     CloseHandle(hFile);
  959.     iBufferCursor = 0;
  960.     // Skip over a any ID3v2 tag
  961.     {
  962.         CIs_ID3v2Tag* pHeader = (CIs_ID3v2Tag*)(pbBuffer + iBufferCursor);
  963.         if(memcmp(pHeader->m_cTAG, "ID3", 3) == 0)
  964.         {
  965.             iBufferCursor += (pHeader->m_cSize_Encoded[0] << 21)
  966.                              | (pHeader->m_cSize_Encoded[1] << 14)
  967.                              | (pHeader->m_cSize_Encoded[2] << 7)
  968.                              | pHeader->m_cSize_Encoded[3];
  969.             iBufferCursor += sizeof(CIs_ID3v2Tag); // count the header
  970.         }
  971.     }
  972.     // Seek to the start of the first frame
  973.     bFoundFrameHeader = FALSE;
  974.     while(iBufferCursor < (dwBufferSize-4))
  975.     {
  976.         if(pbBuffer[iBufferCursor] == 0xFF
  977.                 && (pbBuffer[iBufferCursor+1] & 0xE0) == 0xE0)
  978.         {
  979.             bFoundFrameHeader = TRUE;
  980.             break;
  981.         }
  982.         iBufferCursor++;
  983.     }
  984.     if(bFoundFrameHeader == FALSE)
  985.         return;
  986.     // Work out MPEG version
  987.     if( ((pbBuffer[iBufferCursor+1] >> 3) & 0x3) == 0x3)
  988.         iMPEG_version = 1;
  989.     else
  990.         iMPEG_version = 2;
  991.     // Work out layer
  992.     iLayer = 0x4 - ( (pbBuffer[iBufferCursor+1] >> 1) & 0x3);
  993.     if(iLayer == 0)
  994.         return;
  995.     // Work out stereo
  996.     if( (pbBuffer[iBufferCursor+3]>>6) == 0x3)
  997.         bMono = TRUE;
  998.     else
  999.         bMono = FALSE;
  1000.     // Work out the VBR header should be
  1001.     if(iMPEG_version == 1)
  1002.         iVBRHeader = (iBufferCursor+4) + (bMono ? 17 : 32);
  1003.     else
  1004.         iVBRHeader = (iBufferCursor+4) + (bMono ? 9 : 17);
  1005.     // Is this a VBR file
  1006.     if( (iBufferCursor+iVBRHeader+12) < dwBufferSize
  1007.             && pbBuffer[iVBRHeader]=='X'
  1008.             && pbBuffer[iVBRHeader+1]=='i'
  1009.             && pbBuffer[iVBRHeader+2]=='n'
  1010.             && pbBuffer[iVBRHeader+3]=='g')
  1011.     {
  1012.         int iNumberOfFrames;
  1013.         int iFreq;
  1014.         int iDetailedVersion;
  1015.         const int aryFrequencies[3][3] = {
  1016.                                              {44100, 48000, 32000}, //MPEG 1
  1017.                                              {22050, 24000, 16000}, //MPEG 2
  1018.                                              {32000, 16000,  8000}  //MPEG 2.5
  1019.                                          };
  1020.         if( ((pbBuffer[iBufferCursor+1] >> 3) & 0x3) == 0x3)
  1021.             iDetailedVersion = 1;
  1022.         else if( ((pbBuffer[iBufferCursor+1] >> 3) & 0x3) == 0x2)
  1023.             iDetailedVersion = 2;
  1024.         else
  1025.             iDetailedVersion = 3;
  1026.         // Get the number of frames from the Xing header
  1027.         iNumberOfFrames = (pbBuffer[iVBRHeader+8] << 24)
  1028.                           | (pbBuffer[iVBRHeader+9] << 16)
  1029.                           | (pbBuffer[iVBRHeader+10] << 8)
  1030.                           | pbBuffer[iVBRHeader+11];
  1031.         if( ((pbBuffer[iBufferCursor+2]>>2) & 0x3) == 0x3)
  1032.             return;
  1033.         iFreq = aryFrequencies[iDetailedVersion-1][(pbBuffer[iBufferCursor+2]>>2) & 0x3];
  1034.         CPLI_DecodeLength(pItem, (8 * iNumberOfFrames * 144)/iFreq);
  1035.     }
  1036.     // Work out the bit rate for a CBR file
  1037.     else
  1038.     {
  1039.         const int aryBitRates[2][3][16] = {
  1040.                                               {         //MPEG 2 & 2.5
  1041.                                                   {0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0}, //Layer I
  1042.                                                   {0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0}, //Layer II
  1043.                                                   {0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0}  //Layer III
  1044.                                               },{       //MPEG 1
  1045.                                                   {0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448,0}, //Layer I
  1046.                                                   {0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384,0}, //Layer II
  1047.                                                   {0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,0}  //Layer III
  1048.                                               }
  1049.                                           };
  1050.         iBitRate = aryBitRates[2-iMPEG_version][iLayer-1][pbBuffer[iBufferCursor+2]>>4];
  1051.         if(iBitRate)
  1052.             CPLI_DecodeLength(pItem, (dwFileSize*8)/(iBitRate*1000) );
  1053.     }
  1054. }
  1055. //
  1056. //
  1057. //
  1058. BOOL CPLI_RenameTrack(CP_HPLAYLISTITEM hItem, const CPe_FilenameFormat enFormat)
  1059. {
  1060.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  1061.     char cPath[MAX_PATH];
  1062.     char cNewPath[MAX_PATH];
  1063.     BOOL bMoved;
  1064.     const char* pcExtension;
  1065.     CP_CHECKOBJECT(pItem);
  1066.     strncpy(cPath, pItem->m_pcPath, MAX_PATH);
  1067.     // Remove the filename from the path
  1068.     {
  1069.         int iLastSlashIDX, iCharIDX;
  1070.         iLastSlashIDX = CPC_INVALIDCHAR;
  1071.         for(iCharIDX = 0; cPath[iCharIDX]; iCharIDX++)
  1072.         {
  1073.             if(cPath[iCharIDX] == '\')
  1074.                 iLastSlashIDX = iCharIDX;
  1075.         }
  1076.         if(iLastSlashIDX != CPC_INVALIDCHAR)
  1077.             cPath[iLastSlashIDX] = '';
  1078.     }
  1079.     pcExtension = CPLI_GetExtension(hItem);
  1080.     // Apply the name format
  1081.     {
  1082.         char cNewFilename[MAX_PATH];
  1083.         const char* pcTitle;
  1084.         const char* pcArtist;
  1085.         const char* pcAlbum;
  1086.         if(pItem->m_pcTrackName)
  1087.             pcTitle = pItem->m_pcTrackName;
  1088.         else
  1089.             pcTitle = "<title>";
  1090.         if(pItem->m_pcArtist)
  1091.             pcArtist = pItem->m_pcArtist;
  1092.         else
  1093.             pcArtist = "<title>";
  1094.         if(pItem->m_pcAlbum)
  1095.             pcAlbum = pItem->m_pcAlbum;
  1096.         else
  1097.             pcAlbum = "<album>";
  1098.         switch(enFormat)
  1099.         {
  1100.         case rwsArtistAlbumNumberTitle:
  1101.             sprintf(cNewFilename, "%s - %s - %02d - %s%s", pcArtist, pcAlbum, (int)pItem->m_cTrackNum, pcTitle, pcExtension);
  1102.             break;
  1103.         case rwsArtistNumberTitle:
  1104.             sprintf(cNewFilename, "%s - %02d - %s%s", pcArtist, (int)pItem->m_cTrackNum, pcTitle, pcExtension);
  1105.             break;
  1106.         case rwsAlbumNumberTitle:
  1107.             sprintf(cNewFilename, "%s - %02d - %s%s", pcAlbum, (int)pItem->m_cTrackNum, pcTitle, pcExtension);
  1108.             break;
  1109.         case rwsAlbumNumber:
  1110.             sprintf(cNewFilename, "%s - %02d%s", pcAlbum, (int)pItem->m_cTrackNum, pcExtension);
  1111.             break;
  1112.         case rwsNumberTitle:
  1113.             sprintf(cNewFilename, "%02d - %s%s", (int)pItem->m_cTrackNum, pcTitle, pcExtension);
  1114.             break;
  1115.         case rwsTitle:
  1116.             sprintf(cNewFilename, "%s%s", pcTitle, pcExtension);
  1117.             break;
  1118.         default:
  1119.             CP_FAIL("Unknown rename format");
  1120.         }
  1121.         // Replace illegal chars with _
  1122.         {
  1123.             int iCharIDX;
  1124.             for(iCharIDX = 0; cNewFilename[iCharIDX]; iCharIDX++)
  1125.             {
  1126.                 if(cNewFilename[iCharIDX] == '\'
  1127.                         || cNewFilename[iCharIDX] == '/'
  1128.                         || cNewFilename[iCharIDX] == ':'
  1129.                         || cNewFilename[iCharIDX] == '"')
  1130.                 {
  1131.                     cNewFilename[iCharIDX] = '_';
  1132.                 }
  1133.             }
  1134.         }
  1135.         sprintf(cNewPath, "%s\%s", cPath, cNewFilename);
  1136.     }
  1137.     CP_TRACE2("Rename "%s" to "%s"", pItem->m_pcPath, cNewPath);
  1138.     bMoved = MoveFile(pItem->m_pcPath, cNewPath);
  1139.     if(bMoved)
  1140.     {
  1141.         CPLI_SetPath(pItem, cNewPath);
  1142.         // Update interface
  1143.         CPL_cb_OnItemUpdated(hItem);
  1144.     }
  1145.     return bMoved;
  1146. }
  1147. //
  1148. //
  1149. //
  1150. void CPLI_SetPath(CPs_PlaylistItem* pItem, const char* pcPath)
  1151. {
  1152.     int iCharIDX, iLastSlashIDX;
  1153.     char cFullPath[MAX_PATH];
  1154.     if(pItem->m_pcPath)
  1155.         free(pItem->m_pcPath);
  1156.     // Store the full path to the file if this isn't a stream
  1157.     if(strnicmp(CIC_HTTPHEADER, pcPath, 5) != 0
  1158.             && strnicmp("https:", pcPath, 6) != 0
  1159.             && strnicmp("ftp:", pcPath, 4) != 0)
  1160.     {
  1161.         _fullpath(cFullPath, pcPath, MAX_PATH);
  1162.         STR_AllocSetString(&pItem->m_pcPath, cFullPath, FALSE);
  1163.     }
  1164.     else
  1165.         STR_AllocSetString(&pItem->m_pcPath, pcPath, FALSE);
  1166.     // Get the filename (the string following the last slash)
  1167.     iLastSlashIDX = 0;
  1168.     for(iCharIDX = 0; pItem->m_pcPath[iCharIDX]; iCharIDX++)
  1169.     {
  1170.         if(pItem->m_pcPath[iCharIDX] == '\')
  1171.             iLastSlashIDX = iCharIDX;
  1172.     }
  1173.     pItem->m_pcFilename = pItem->m_pcPath + iLastSlashIDX + 1;
  1174. }
  1175. //
  1176. //
  1177. //
  1178. const char* CPLI_GetExtension(const CP_HPLAYLISTITEM hItem)
  1179. {
  1180.     CPs_PlaylistItem* pItem = (CPs_PlaylistItem*)hItem;
  1181.     int iCharIDX;
  1182.     const char* pcLastDot;
  1183.     CP_CHECKOBJECT(pItem);
  1184.     pcLastDot = NULL;
  1185.     for(iCharIDX = 0; pItem->m_pcPath[iCharIDX]; iCharIDX++)
  1186.     {
  1187.         if(pItem->m_pcPath[iCharIDX] == '.')
  1188.             pcLastDot = pItem->m_pcPath + iCharIDX;
  1189.         // If there is a directory name with a dot in it we don't want that!
  1190.         else if(pItem->m_pcPath[iCharIDX] == '\')
  1191.             pcLastDot = NULL;
  1192.     }
  1193.     // Ensure the string is valid
  1194.     if(!pcLastDot)
  1195.         pcLastDot = "";
  1196.     return pcLastDot;
  1197. }
  1198. //
  1199. //
  1200. //
  1201. /*
  1202. void CPLI_OGG_SkipOverTab(FILE* pFile)
  1203. {
  1204.     CIs_ID3v2Tag tag;
  1205.     int iStreamStart = 0;
  1206.     memset(&tag, 0, sizeof(tag));
  1207.     fread(&tag, sizeof(tag), 1, pFile);
  1208.     if(memcmp(tag.m_cTAG, "ID3", 3) == 0)
  1209.     {
  1210.         iStreamStart = sizeof(CIs_ID3v2Tag);
  1211.         iStreamStart += (tag.m_cSize_Encoded[0] << 21)
  1212.                         | (tag.m_cSize_Encoded[1] << 14)
  1213.                         | (tag.m_cSize_Encoded[2] << 7)
  1214.                         | tag.m_cSize_Encoded[3];
  1215.     }
  1216.     fseek(pFile, iStreamStart, SEEK_SET);
  1217. }*/
  1218. //
  1219. //
  1220. //
  1221. /*void CPLI_OGG_DecodeString(char** ppcString, const char* pcNewValue)
  1222. {
  1223.     int iStringLength;
  1224.     if(*ppcString)
  1225.         free(*ppcString);
  1226.     iStringLength = strlen(pcNewValue);
  1227.     *ppcString = malloc(iStringLength + 1);
  1228.     memcpy(*ppcString, pcNewValue, iStringLength+1);
  1229. }*/
  1230. //
  1231. //
  1232. //
  1233. void CPLI_ShrinkFile(HANDLE hFile, const DWORD dwStartOffset, const unsigned int iNumBytes)
  1234. {
  1235.     BYTE pBuffer[0x10000];
  1236.     DWORD dwLength;
  1237.     DWORD dwBytesTransferred;
  1238.     DWORD dwCursor;
  1239.     CP_TRACE1("Shrunking file by %d bytes", iNumBytes);
  1240.     dwLength = GetFileSize(hFile, NULL);
  1241.     CP_ASSERT( (dwStartOffset+iNumBytes) < dwLength);
  1242.     dwCursor = dwStartOffset;
  1243.     while((dwCursor+iNumBytes) < dwLength)
  1244.     {
  1245.         unsigned int iChunkSize;
  1246.         iChunkSize = 0x10000;
  1247.         if(iChunkSize > dwLength-(dwCursor+iNumBytes) )
  1248.             iChunkSize = dwLength-(dwCursor+iNumBytes);
  1249.         SetFilePointer(hFile, dwCursor + iNumBytes, NULL, FILE_BEGIN);
  1250.         ReadFile(hFile, pBuffer, iChunkSize, &dwBytesTransferred, NULL);
  1251.         CP_ASSERT(dwBytesTransferred == iChunkSize);
  1252.         SetFilePointer(hFile, dwCursor, NULL, FILE_BEGIN);
  1253.         WriteFile(hFile, pBuffer, iChunkSize, &dwBytesTransferred, NULL);
  1254.         dwCursor += iChunkSize;
  1255.     }
  1256.     SetFilePointer(hFile, dwLength - iNumBytes, NULL, FILE_BEGIN);
  1257.     SetEndOfFile(hFile);
  1258. }
  1259. //
  1260. //
  1261. //
  1262. BOOL CPLI_GrowFile(HANDLE hFile, const DWORD dwStartOffset, const unsigned int iNumBytes)
  1263. {
  1264.     DWORD dwFileSize;
  1265.     unsigned int iFileCursor;
  1266.     DWORD dwBytesTransferred;
  1267.     BYTE* pbReadBlock[0x10000];
  1268.     dwFileSize = GetFileSize(hFile, NULL);
  1269.     CP_TRACE1("Enlarging file by %d bytes", iNumBytes);
  1270.     // Try to write extra data to end of file - if we fail then clip the file and return
  1271.     // (so that we don't corrupt the file in short of space situations)
  1272.     {
  1273.         BYTE* pbExtra;
  1274.         pbExtra = (BYTE*)malloc(iNumBytes);
  1275.         memset(pbExtra, 0, iNumBytes);
  1276.         SetFilePointer(hFile, dwFileSize + iNumBytes, NULL, FILE_BEGIN);
  1277.         WriteFile(hFile, pbExtra, iNumBytes, &dwBytesTransferred, NULL);
  1278.         if(dwBytesTransferred != iNumBytes)
  1279.         {
  1280.             // Failed - clip file again and abort tag write
  1281.             SetFilePointer(hFile, dwFileSize, NULL, FILE_BEGIN);
  1282.             SetEndOfFile(hFile);
  1283.             return FALSE;
  1284.         }
  1285.     }
  1286.     // Enlarge tag
  1287.     iFileCursor = dwFileSize;
  1288.     while(iFileCursor > dwStartOffset)
  1289.     {
  1290.         unsigned int iBlockSize;
  1291.         iBlockSize = 0x10000;
  1292.         if( (iFileCursor - dwStartOffset) < iBlockSize)
  1293.             iBlockSize = iFileCursor - dwStartOffset;
  1294.         // Read a chunk
  1295.         SetFilePointer(hFile, iFileCursor - iBlockSize, NULL, FILE_BEGIN);
  1296.         ReadFile(hFile, pbReadBlock, iBlockSize, &dwBytesTransferred, NULL);
  1297.         CP_ASSERT(dwBytesTransferred == iBlockSize);
  1298.         // Write chunk at offsetted position
  1299.         SetFilePointer(hFile, iFileCursor - iBlockSize + iNumBytes, NULL, FILE_BEGIN);
  1300.         WriteFile(hFile, pbReadBlock, iBlockSize, &dwBytesTransferred, NULL);
  1301.         CP_ASSERT(dwBytesTransferred == iBlockSize);
  1302.         iFileCursor -= iBlockSize;
  1303.     }
  1304.     return TRUE;
  1305. }
  1306. //
  1307. //
  1308. //