fixed: quirks with album information in file view
[xbmc:xbmc-antiquated.git] / xbmc / FileItem.cpp
1 /*
2  *      Copyright (C) 2005-2007 Team XboxMediaCenter
3  *      http://www.xboxmediacenter.com
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with GNU Make; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "stdafx.h"
23 #include "FileItem.h"
24 #include "Util.h"
25 #include "Picture.h"
26 #include "PlayListFactory.h"
27 #include "Shortcut.h"
28 #include "Crc32.h"
29 #include "FileSystem/DirectoryCache.h"
30 #include "FileSystem/StackDirectory.h"
31 #include "FileSystem/FileCurl.h"
32 #include "FileSystem/MultiPathDirectory.h"
33 #include "FileSystem/MusicDatabaseDirectory.h"
34 #include "FileSystem/VideoDatabaseDirectory.h"
35 #include "musicInfoTagLoaderFactory.h"
36 #include "CueDocument.h"
37 #include "utils/fstrcmp.h"
38 #include "VideoDatabase.h"
39 #include "MusicDatabase.h"
40 #include "SortFileItem.h"
41 #include "utils/TuxBoxUtil.h"
42
43 using namespace std;
44 using namespace XFILE;
45 using namespace DIRECTORY;
46 using namespace PLAYLIST;
47 using namespace MUSIC_INFO;
48
49 CFileItem::CFileItem(const CSong& song)
50 {
51   m_musicInfoTag = NULL;
52   m_videoInfoTag = NULL;
53   m_pictureInfoTag = NULL;
54   Reset();
55   m_strLabel = song.strTitle;
56   m_strPath = song.strFileName;
57   GetMusicInfoTag()->SetSong(song);
58   m_lStartOffset = song.iStartOffset;
59   m_lEndOffset = song.iEndOffset;
60   m_strThumbnailImage = song.strThumb;
61 }
62
63 CFileItem::CFileItem(const CStdString &path, const CAlbum& album)
64 {
65   m_musicInfoTag = NULL;
66   m_videoInfoTag = NULL;
67   m_pictureInfoTag = NULL;
68   Reset();
69   m_strLabel = album.strAlbum;
70   m_strPath = path;
71   m_bIsFolder = true;
72   m_strLabel2 = album.strArtist;
73   CUtil::AddSlashAtEnd(m_strPath);
74   GetMusicInfoTag()->SetAlbum(album);
75   if (album.thumbURL.m_url.size() > 0)
76     m_strThumbnailImage = album.thumbURL.m_url[0].m_url;
77   else
78     m_strThumbnailImage.clear();
79 }
80
81 CFileItem::CFileItem(const CVideoInfoTag& movie)
82 {
83   m_musicInfoTag = NULL;
84   m_videoInfoTag = NULL;
85   m_pictureInfoTag = NULL;
86   Reset();
87   m_strLabel = movie.m_strTitle;
88   if (movie.m_strFileNameAndPath.IsEmpty())
89   {
90     m_strPath = movie.m_strPath;
91     CUtil::AddSlashAtEnd(m_strPath);
92     m_bIsFolder = true;
93   }
94   else
95   {
96     m_strPath = movie.m_strFileNameAndPath;
97     m_bIsFolder = false;
98   }
99   *GetVideoInfoTag() = movie;
100   FillInDefaultIcon();
101   SetVideoThumb();
102   SetInvalid();
103 }
104
105 CFileItem::CFileItem(const CArtist& artist)
106 {
107   m_musicInfoTag = NULL;
108   m_videoInfoTag = NULL;
109   m_pictureInfoTag = NULL;
110   Reset();
111   m_strLabel = artist.strArtist;
112   m_strPath = artist.strArtist;
113   m_bIsFolder = true;
114   CUtil::AddSlashAtEnd(m_strPath);
115   GetMusicInfoTag()->SetArtist(artist.strArtist);
116 }
117
118 CFileItem::CFileItem(const CGenre& genre)
119 {
120   m_musicInfoTag = NULL;
121   m_videoInfoTag = NULL;
122   m_pictureInfoTag = NULL;
123   Reset();
124   m_strLabel = genre.strGenre;
125   m_strPath = genre.strGenre;
126   m_bIsFolder = true;
127   CUtil::AddSlashAtEnd(m_strPath);
128   GetMusicInfoTag()->SetGenre(genre.strGenre);
129 }
130
131 CFileItem::CFileItem(const CFileItem& item)
132 {
133   m_musicInfoTag = NULL;
134   m_videoInfoTag = NULL;
135   m_pictureInfoTag = NULL;
136   *this = item;
137 }
138
139 CFileItem::CFileItem(const CGUIListItem& item)
140 {
141   m_musicInfoTag = NULL;
142   m_videoInfoTag = NULL;
143   m_pictureInfoTag = NULL;
144   Reset();
145   // not particularly pretty, but it gets around the issue of Reset() defaulting
146   // parameters in the CGUIListItem base class.
147   *((CGUIListItem *)this) = item;
148 }
149
150 CFileItem::CFileItem(void)
151 {
152   m_musicInfoTag = NULL;
153   m_videoInfoTag = NULL;
154   m_pictureInfoTag = NULL;
155   Reset();
156 }
157
158 CFileItem::CFileItem(const CStdString& strLabel)
159     : CGUIListItem()
160 {
161   m_musicInfoTag = NULL;
162   m_videoInfoTag = NULL;
163   m_pictureInfoTag = NULL;
164   Reset();
165   SetLabel(strLabel);
166 }
167
168 CFileItem::CFileItem(const CStdString& strPath, bool bIsFolder)
169 {
170   m_musicInfoTag = NULL;
171   m_videoInfoTag = NULL;
172   m_pictureInfoTag = NULL;
173   Reset();
174   m_strPath = strPath;
175   m_bIsFolder = bIsFolder;
176 #ifdef DEBUG
177   // tuxbox urls cannot have a / at end
178   if (m_bIsFolder && !m_strPath.IsEmpty() && !IsFileFolder() && !CUtil::IsTuxBox(m_strPath))
179     ASSERT(CUtil::HasSlashAtEnd(m_strPath));
180 #endif
181
182   if (m_bIsFolder)
183     CUtil::AddSlashAtEnd(m_strPath);
184 }
185
186 CFileItem::CFileItem(const CShare& share)
187 {
188   m_musicInfoTag = NULL;
189   m_videoInfoTag = NULL;
190   m_pictureInfoTag = NULL;
191   Reset();
192   m_bIsFolder = true;
193   m_bIsShareOrDrive = true;
194   m_strPath = share.strPath;
195   CUtil::AddSlashAtEnd(m_strPath);
196   m_strLabel = share.strName;
197   if (share.strStatus.size())
198     m_strLabel.Format("%s (%s)", share.strName.c_str(), share.strStatus.c_str());
199   m_iLockMode = share.m_iLockMode;
200   m_strLockCode = share.m_strLockCode;
201   m_iHasLock = share.m_iHasLock;
202   m_iBadPwdCount = share.m_iBadPwdCount;
203   m_iDriveType = share.m_iDriveType;
204   m_strThumbnailImage = share.m_strThumbnailImage;
205   SetLabelPreformated(true);
206 }
207
208 CFileItem::~CFileItem(void)
209 {
210   if (m_musicInfoTag)
211     delete m_musicInfoTag;
212   if (m_videoInfoTag)
213     delete m_videoInfoTag;
214   if (m_pictureInfoTag)
215     delete m_pictureInfoTag;
216
217   m_musicInfoTag = NULL;
218   m_videoInfoTag = NULL;
219   m_pictureInfoTag = NULL;
220 }
221
222 const CFileItem& CFileItem::operator=(const CFileItem& item)
223 {
224   if (this == &item) return * this;
225   CGUIListItem::operator=(item);
226   m_bLabelPreformated=item.m_bLabelPreformated;
227   FreeMemory();
228   m_strPath = item.m_strPath;
229 #ifdef DEBUG
230   if (m_bIsFolder && !m_strPath.IsEmpty() && !IsFileFolder())  // should root paths be "/" ?
231     ASSERT(CUtil::HasSlashAtEnd(m_strPath));
232 #endif
233   m_bIsParentFolder = item.m_bIsParentFolder;
234   m_iDriveType = item.m_iDriveType;
235   m_bIsShareOrDrive = item.m_bIsShareOrDrive;
236   m_dateTime = item.m_dateTime;
237   m_dwSize = item.m_dwSize;
238   if (item.HasMusicInfoTag())
239   {
240     m_musicInfoTag = GetMusicInfoTag();
241     if (m_musicInfoTag)
242       *m_musicInfoTag = *item.m_musicInfoTag;
243   }
244   else
245   {
246     if (m_musicInfoTag)
247       delete m_musicInfoTag;
248
249     m_musicInfoTag = NULL;
250   }
251
252   if (item.HasVideoInfoTag())
253   {
254     m_videoInfoTag = GetVideoInfoTag();
255     if (m_videoInfoTag)
256       *m_videoInfoTag = *item.m_videoInfoTag;
257   }
258   else
259   {
260     if (m_videoInfoTag)
261       delete m_videoInfoTag;
262
263     m_videoInfoTag = NULL;
264   }
265
266   if (item.HasPictureInfoTag())
267   {
268     m_pictureInfoTag = GetPictureInfoTag();
269     if (m_pictureInfoTag)
270       *m_pictureInfoTag = *item.m_pictureInfoTag;
271   }
272   else
273   {
274     if (m_pictureInfoTag)
275       delete m_pictureInfoTag;
276
277     m_pictureInfoTag = NULL;
278   }
279
280   m_lStartOffset = item.m_lStartOffset;
281   m_lEndOffset = item.m_lEndOffset;
282   m_strDVDLabel = item.m_strDVDLabel;
283   m_strTitle = item.m_strTitle;
284   m_iprogramCount = item.m_iprogramCount;
285   m_idepth = item.m_idepth;
286   m_iLockMode = item.m_iLockMode;
287   m_strLockCode = item.m_strLockCode;
288   m_iHasLock = item.m_iHasLock;
289   m_iBadPwdCount = item.m_iBadPwdCount;
290   m_bCanQueue=item.m_bCanQueue;
291   m_contenttype = item.m_contenttype;
292   m_extrainfo = item.m_extrainfo;
293   return *this;
294 }
295
296 void CFileItem::Reset()
297 {
298   m_strLabel2.Empty();
299   m_strLabel.Empty();
300   m_bLabelPreformated=false;
301   FreeIcons();
302   m_overlayIcon = ICON_OVERLAY_NONE;
303   m_bSelected = false;
304   m_strDVDLabel.Empty();
305   m_strTitle.Empty();
306   m_strPath.Empty();
307   m_dwSize = 0;
308   m_bIsFolder = false;
309   m_bIsParentFolder=false;
310   m_bIsShareOrDrive = false;
311   m_dateTime.Reset();
312   m_iDriveType = SHARE_TYPE_UNKNOWN;
313   m_lStartOffset = 0;
314   m_lEndOffset = 0;
315   m_iprogramCount = 0;
316   m_idepth = 1;
317   m_iLockMode = LOCK_MODE_EVERYONE;
318   m_strLockCode = "";
319   m_iBadPwdCount = 0;
320   m_iHasLock = 0;
321   m_bCanQueue=true;
322   m_contenttype = "";
323   if (m_musicInfoTag)
324     delete m_musicInfoTag;
325   m_musicInfoTag=NULL;
326   if (m_videoInfoTag)
327     delete m_videoInfoTag;
328   m_videoInfoTag=NULL;
329   if (m_pictureInfoTag)
330     delete m_pictureInfoTag;
331   m_pictureInfoTag=NULL;
332   m_extrainfo.Empty();
333   SetInvalid();
334 }
335
336 void CFileItem::Serialize(CArchive& ar)
337 {
338   CGUIListItem::Serialize(ar);
339
340   if (ar.IsStoring())
341   {
342     ar << m_bIsParentFolder;
343     ar << m_bLabelPreformated;
344     ar << m_strPath;
345     ar << m_bIsShareOrDrive;
346     ar << m_iDriveType;
347     ar << m_dateTime;
348     ar << m_dwSize;
349     ar << m_strDVDLabel;
350     ar << m_strTitle;
351     ar << m_iprogramCount;
352     ar << m_idepth;
353     ar << m_lStartOffset;
354     ar << m_lEndOffset;
355     ar << m_iLockMode;
356     ar << m_strLockCode;
357     ar << m_iBadPwdCount;
358
359     ar << m_bCanQueue;
360     ar << m_contenttype;
361     ar << m_extrainfo;
362
363     if (m_musicInfoTag)
364     {
365       ar << 1;
366       ar << *m_musicInfoTag;
367     }
368     else
369       ar << 0;
370     if (m_videoInfoTag)
371     {
372       ar << 1;
373       ar << *m_videoInfoTag;
374     }
375     else 
376       ar << 0;
377     if (m_pictureInfoTag)
378     {
379       ar << 1;
380       ar << *m_pictureInfoTag;
381     }
382     else 
383       ar << 0;
384   }
385   else
386   {
387     ar >> m_bIsParentFolder;
388     ar >> m_bLabelPreformated;
389     ar >> m_strPath;
390     ar >> m_bIsShareOrDrive;
391     ar >> m_iDriveType;
392     ar >> m_dateTime;
393     ar >> m_dwSize;
394     ar >> m_strDVDLabel;
395     ar >> m_strTitle;
396     ar >> m_iprogramCount;
397     ar >> m_idepth;
398     ar >> m_lStartOffset;
399     ar >> m_lEndOffset;
400     ar >> m_iLockMode;
401     ar >> m_strLockCode;
402     ar >> m_iBadPwdCount;
403
404     ar >> m_bCanQueue;
405     ar >> m_contenttype;
406     ar >> m_extrainfo;
407
408     int iType;
409     ar >> iType;
410     if (iType == 1)
411       ar >> *GetMusicInfoTag();
412     ar >> iType;
413     if (iType == 1)
414       ar >> *GetVideoInfoTag();
415     ar >> iType;
416     if (iType == 1)
417       ar >> *GetPictureInfoTag();
418     SetInvalid();
419   }
420 }
421
422 bool CFileItem::IsVideo() const
423 {
424   /* check preset content type */
425   if( m_contenttype.Left(6).Equals("video/") )
426     return true;
427   
428   if (m_strPath.Left(7).Equals("tuxbox:")) 
429     return true;
430
431   if (m_strPath.Left(10).Equals("hdhomerun:"))
432     return true;
433
434   CStdString extension;
435   if( m_contenttype.Left(12).Equals("application/") )
436   { /* check for some standard types */
437     extension = m_contenttype.Mid(12);
438     if( extension.Equals("ogg")
439      || extension.Equals("mp4")
440      || extension.Equals("mxf") )
441      return true;
442   }
443
444   CUtil::GetExtension(m_strPath, extension);
445
446   if (extension.IsEmpty())
447     return false;
448
449   extension.ToLower();
450
451   if (g_stSettings.m_videoExtensions.Find(extension) != -1)
452     return true;
453
454   return false;
455 }
456
457 bool CFileItem::IsAudio() const
458 {
459   if (IsCDDA()) return true;
460   if (IsShoutCast() && !m_bIsFolder) return true;
461   if (IsLastFM() && !m_bIsFolder) return true;
462
463   /* check preset content type */
464   if( m_contenttype.Left(6).Equals("audio/") )
465     return true;
466
467   CStdString extension;
468   if( m_contenttype.Left(12).Equals("application/") )
469   { /* check for some standard types */
470     extension = m_contenttype.Mid(12);
471     if( extension.Equals("ogg")
472      || extension.Equals("mp4")
473      || extension.Equals("mxf") )
474      return true;
475   }
476
477   CUtil::GetExtension(m_strPath, extension);
478
479   if (extension.IsEmpty())
480     return false;
481
482   extension.ToLower();
483   if (g_stSettings.m_musicExtensions.Find(extension) != -1)
484     return true;
485
486   return false;
487 }
488
489 bool CFileItem::IsPicture() const
490 {
491   if( m_contenttype.Left(6).Equals("image/") )
492     return true;
493
494   CStdString extension;
495   CUtil::GetExtension(m_strPath, extension);
496
497   if (extension.IsEmpty())
498     return false;
499
500   extension.ToLower();
501   if (g_stSettings.m_pictureExtensions.Find(extension) != -1)
502     return true;
503
504   if (extension == ".tbn")
505     return true;
506
507   return false;
508 }
509
510 bool CFileItem::IsCUESheet() const
511 {
512   CStdString strExtension;
513   CUtil::GetExtension(m_strPath, strExtension);
514   return (strExtension.CompareNoCase(".cue") == 0);
515 }
516
517 bool CFileItem::IsShoutCast() const
518 {
519   if (strstr(m_strPath.c_str(), "shout:") ) return true;
520   return false;
521 }
522 bool CFileItem::IsLastFM() const
523 {
524   if (strstr(m_strPath.c_str(), "lastfm:") ) return true;
525   return false;
526 }
527
528 bool CFileItem::IsInternetStream() const
529 {
530   CURL url(m_strPath);
531   CStdString strProtocol = url.GetProtocol();
532   strProtocol.ToLower();
533
534   if (strProtocol.size() == 0)
535     return false;
536
537   if (strProtocol == "shout" || strProtocol == "mms" ||
538       strProtocol == "http" || /*strProtocol == "ftp" ||*/
539       strProtocol == "rtsp" || strProtocol == "rtp" ||
540       strProtocol == "udp"  || strProtocol == "lastfm" ||
541       strProtocol == "https")
542     return true;
543
544   return false;
545 }
546
547 bool CFileItem::IsFileFolder() const
548 {
549   return (
550     m_bIsFolder && (
551     IsPluginFolder() ||
552     IsSmartPlayList() ||
553     IsPlayList() ||
554     IsZIP() ||
555     IsRAR() ||
556     IsType("ogg") ||
557     IsType("nsf") ||
558     IsType("sid") ||
559     IsShoutCast()
560     )
561     );
562 }
563
564
565 bool CFileItem::IsSmartPlayList() const
566 {
567   CStdString strExtension;
568   CUtil::GetExtension(m_strPath, strExtension);
569   strExtension.ToLower();
570   return (strExtension == ".xsp");
571 }
572
573 bool CFileItem::IsPlayList() const
574 {
575   return CPlayListFactory::IsPlaylist(m_strPath);
576 }
577
578 bool CFileItem::IsPythonScript() const
579 {
580   return CUtil::GetExtension(m_strPath).Equals(".py", false);
581 }
582
583 bool CFileItem::IsXBE() const
584 {
585   return CUtil::GetExtension(m_strPath).Equals(".xbe", false);
586 }
587
588 bool CFileItem::IsType(const char *ext) const
589 {
590   return CUtil::GetExtension(m_strPath).Equals(ext, false);
591 }
592
593 bool CFileItem::IsDefaultXBE() const
594 {
595   CStdString filename = CUtil::GetFileName(m_strPath);
596   if (filename.Equals("default.xbe")) return true;
597   return false;
598 }
599
600 bool CFileItem::IsShortCut() const
601 {
602   return CUtil::GetExtension(m_strPath).Equals(".cut", false);
603 }
604
605 bool CFileItem::IsNFO() const
606 {
607   return CUtil::GetExtension(m_strPath).Equals(".nfo", false);
608 }
609
610 bool CFileItem::IsDVDImage() const
611 {
612   CStdString strExtension;
613   CUtil::GetExtension(m_strPath, strExtension);
614   if (strExtension.Equals(".img") || strExtension.Equals(".iso")) return true;
615   return false;
616 }
617
618 bool CFileItem::IsDVDFile(bool bVobs /*= true*/, bool bIfos /*= true*/) const
619 {
620   CStdString strFileName = CUtil::GetFileName(m_strPath);
621   if (bIfos)
622   {
623     if (strFileName.Equals("video_ts.ifo")) return true;
624     if (strFileName.Left(4).Equals("vts_") && strFileName.Right(6).Equals("_0.ifo") && strFileName.length() == 12) return true;
625   }
626   if (bVobs)
627   {
628     if (strFileName.Equals("video_ts.vob")) return true;
629     if (strFileName.Left(4).Equals("vts_") && strFileName.Right(4).Equals(".vob")) return true;
630   }
631
632   return false;
633 }
634
635 bool CFileItem::IsRAR() const
636 {
637   CStdString strExtension;
638   CUtil::GetExtension(m_strPath, strExtension);
639   if ( (strExtension.CompareNoCase(".rar") == 0) || ((strExtension.Equals(".001") && m_strPath.Mid(m_strPath.length()-7,7).CompareNoCase(".ts.001"))) ) return true; // sometimes the first rar is named .001
640   return false;
641 }
642
643 bool CFileItem::IsZIP() const
644 {
645   return CUtil::GetExtension(m_strPath).Equals(".zip", false);
646 }
647
648 bool CFileItem::IsCBZ() const
649 {
650   return CUtil::GetExtension(m_strPath).Equals(".cbz", false);
651 }
652
653 bool CFileItem::IsCBR() const
654 {
655   return CUtil::GetExtension(m_strPath).Equals(".cbr", false);
656 }
657
658 bool CFileItem::IsStack() const
659 {
660   return CUtil::IsStack(m_strPath);
661 }
662
663 bool CFileItem::IsPluginFolder() const
664 {
665   CURL url(m_strPath);
666   return url.GetProtocol().Equals("plugin") && !url.GetFileName().IsEmpty();
667 }
668
669 bool CFileItem::IsMultiPath() const
670 {
671   return CUtil::IsMultiPath(m_strPath);
672 }
673
674 bool CFileItem::IsCDDA() const
675 {
676   return CUtil::IsCDDA(m_strPath);
677 }
678
679 bool CFileItem::IsDVD() const
680 {
681   return CUtil::IsDVD(m_strPath);
682 }
683
684 bool CFileItem::IsOnDVD() const
685 {
686   return CUtil::IsOnDVD(m_strPath);
687 }
688
689 bool CFileItem::IsOnLAN() const
690 {
691   return CUtil::IsOnLAN(m_strPath);
692 }
693
694 bool CFileItem::IsISO9660() const
695 {
696   return CUtil::IsISO9660(m_strPath);
697 }
698
699 bool CFileItem::IsRemote() const
700 {
701   return CUtil::IsRemote(m_strPath);
702 }
703
704 bool CFileItem::IsSmb() const
705 {
706   return CUtil::IsSmb(m_strPath);
707 }
708
709 bool CFileItem::IsDAAP() const
710 {
711   return CUtil::IsDAAP(m_strPath);
712 }
713 bool CFileItem::IsTuxBox() const
714 {
715   return CUtil::IsTuxBox(m_strPath);
716 }
717
718 bool CFileItem::IsMythTV() const
719 {
720   return CUtil::IsMythTV(m_strPath);
721 }
722
723 bool CFileItem::IsHD() const
724 {
725   return CUtil::IsHD(m_strPath);
726 }
727
728 bool CFileItem::IsMusicDb() const
729 {
730   if (strstr(m_strPath.c_str(), "musicdb:") ) return true;
731   return false;
732 }
733
734 bool CFileItem::IsVideoDb() const
735 {
736   if (strstr(m_strPath.c_str(), "videodb:") ) return true;
737   return false;
738 }
739
740 bool CFileItem::IsVirtualDirectoryRoot() const
741 {
742   return (m_bIsFolder && m_strPath.IsEmpty());
743 }
744
745 bool CFileItem::IsMemoryUnit() const
746 {
747   CURL url(m_strPath);
748   return url.GetProtocol().Left(3).Equals("mem");
749 }
750
751 bool CFileItem::IsRemovable() const
752 {
753   return IsOnDVD() || IsCDDA() || IsMemoryUnit();
754 }
755
756 bool CFileItem::IsReadOnly() const
757 {
758   if (IsParentFolder()) return true;
759   if (m_bIsShareOrDrive) return true;
760   return !CUtil::SupportsFileOperations(m_strPath);
761 }
762
763 void CFileItem::FillInDefaultIcon()
764 {
765   //CLog::Log(LOGINFO, "FillInDefaultIcon(%s)", pItem->GetLabel().c_str());
766   // find the default icon for a file or folder item
767   // for files this can be the (depending on the file type)
768   //   default picture for photo's
769   //   default picture for songs
770   //   default picture for videos
771   //   default picture for shortcuts
772   //   default picture for playlists
773   //   or the icon embedded in an .xbe
774   //
775   // for folders
776   //   for .. folders the default picture for parent folder
777   //   for other folders the defaultFolder.png
778
779   CStdString strThumb;
780   CStdString strExtension;
781   if (GetIconImage() == "")
782   {
783     if (!m_bIsFolder)
784     {
785       if ( IsPlayList() )
786       {
787         SetIconImage("defaultPlaylist.png");
788       }
789       else if ( IsPicture() )
790       {
791         // picture
792         SetIconImage("defaultPicture.png");
793       }
794       else if ( IsXBE() )
795       {
796         // xbe
797         SetIconImage("defaultProgram.png");
798       }
799       else if ( IsAudio() )
800       {
801         // audio
802         SetIconImage("defaultAudio.png");
803       }
804       else if ( IsVideo() )
805       {
806         // video
807         SetIconImage("defaultVideo.png");
808       }
809       else if ( IsShortCut() && !IsLabelPreformated() )
810       {
811         // shortcut
812         CStdString strDescription;
813         CStdString strFName;
814         strFName = CUtil::GetFileName(m_strPath);
815
816         int iPos = strFName.ReverseFind(".");
817         strDescription = strFName.Left(iPos);
818         SetLabel(strDescription);
819         SetIconImage("defaultShortcut.png");
820       }
821       else if ( IsPythonScript() )
822       {
823         SetIconImage("DefaultScript.png");
824       }
825       else
826       {
827         // default icon for unknown file type
828         SetIconImage("defaultFile.png");
829       }
830     }
831     else
832     {
833       if ( IsPlayList() )
834       {
835         SetIconImage("defaultPlaylist.png");
836       }
837       else if (IsParentFolder())
838       {
839         SetIconImage("defaultFolderBack.png");
840       }
841       else
842       {
843         SetIconImage("defaultFolder.png");
844       }
845     }
846   }
847   // Set the icon overlays (if applicable)
848   if (!HasOverlay())
849   {
850     if (CUtil::IsInRAR(m_strPath))
851       SetOverlayImage(CGUIListItem::ICON_OVERLAY_RAR);
852     else if (CUtil::IsInZIP(m_strPath))
853       SetOverlayImage(CGUIListItem::ICON_OVERLAY_ZIP);
854   }
855 }
856
857 CStdString CFileItem::GetCachedArtistThumb()
858 {
859   Crc32 crc;
860   crc.ComputeFromLowerCase("artist" + GetLabel());
861   CStdString cachedThumb;
862   cachedThumb.Format("%s\\%08x.tbn", g_settings.GetMusicArtistThumbFolder().c_str(), (unsigned __int32)crc);
863   return cachedThumb;
864 }
865
866 CStdString CFileItem::GetCachedProfileThumb()
867 {
868   Crc32 crc;
869   crc.ComputeFromLowerCase("profile" + m_strPath);
870   CStdString cachedThumb;
871   cachedThumb.Format("%s\\Thumbnails\\Profiles\\%08x.tbn", g_settings.GetUserDataFolder().c_str(), (unsigned __int32)crc);
872   return cachedThumb;
873 }
874
875 CStdString CFileItem::GetCachedSeasonThumb()
876 {
877   Crc32 crc;
878   crc.ComputeFromLowerCase("season" + GetVideoInfoTag()->m_strPath+GetLabel());
879   CStdString hex;
880   hex.Format("%08x", (__int32)crc);
881   CStdString cachedThumb;
882   cachedThumb.Format("%s\\%c\\%08x.tbn", g_settings.GetVideoThumbFolder().c_str(), hex[0], (unsigned __int32)crc);
883   return cachedThumb;
884 }
885
886 CStdString CFileItem::GetCachedActorThumb()
887 {
888   Crc32 crc;
889   crc.ComputeFromLowerCase("actor" + GetLabel());
890   CStdString hex;
891   hex.Format("%08x", (__int32)crc);
892   CStdString cachedThumb;
893   cachedThumb.Format("%s\\%c\\%08x.tbn", g_settings.GetVideoThumbFolder().c_str(), hex[0], (unsigned __int32)crc);
894   return cachedThumb;
895 }
896
897 void CFileItem::SetCachedArtistThumb()
898 {
899   CStdString thumb(GetCachedArtistThumb());
900   if (CFile::Exists(thumb))
901   {
902     // found it, we are finished.
903     SetThumbnailImage(thumb);
904 //    SetIconImage(strThumb);
905   }
906 }
907
908 // set the album thumb for a file or folder
909 void CFileItem::SetMusicThumb(bool alwaysCheckRemote /* = true */)
910 {
911   if (HasThumbnail()) return;
912   SetCachedMusicThumb();
913   if (!HasThumbnail())
914     SetUserMusicThumb(alwaysCheckRemote);
915 }
916
917 void CFileItem::SetCachedSeasonThumb()
918 {
919   CStdString thumb(GetCachedSeasonThumb());
920   if (CFile::Exists(thumb))
921   {
922     // found it, we are finished.
923     SetThumbnailImage(thumb);
924   }
925 }
926
927 void CFileItem::RemoveExtension()
928 {
929   if (m_bIsFolder)
930     return ;
931   CStdString strLabel = GetLabel();
932   CUtil::RemoveExtension(strLabel);
933   SetLabel(strLabel);
934 }
935
936 void CFileItem::CleanFileName()
937 {
938   if (m_bIsFolder)
939     return ;
940   CStdString strLabel = GetLabel();
941   CUtil::CleanFileName(strLabel);
942   SetLabel(strLabel);
943 }
944
945 void CFileItem::SetLabel(const CStdString &strLabel)
946 {
947   if (strLabel=="..")
948   {
949     m_bIsParentFolder=true;
950     m_bIsFolder=true;
951     SetLabelPreformated(true);
952   }
953   CGUIListItem::SetLabel(strLabel);
954 }
955
956 void CFileItem::SetFileSizeLabel()
957 {
958   if( m_bIsFolder && m_dwSize == 0 )
959     SetLabel2("");
960   else
961     SetLabel2(StringUtils::SizeToString(m_dwSize));
962 }
963
964 CURL CFileItem::GetAsUrl() const
965 {
966   return CURL(m_strPath);
967 }
968
969 bool CFileItem::CanQueue() const
970 {
971   return m_bCanQueue;
972 }
973
974 void CFileItem::SetCanQueue(bool bYesNo)
975 {
976   m_bCanQueue=bYesNo;
977 }
978
979 bool CFileItem::IsParentFolder() const
980 {
981   return m_bIsParentFolder;
982 }
983
984 const CStdString& CFileItem::GetContentType() const
985 {
986   if( m_contenttype.IsEmpty() )
987   {
988     // discard const qualifyier
989     CStdString& m_ref = (CStdString&)m_contenttype;
990
991     if( m_bIsFolder )
992       m_ref = "x-directory/normal";
993     else if( m_strPath.Left(8).Equals("shout://")
994           || m_strPath.Left(7).Equals("http://")
995           || m_strPath.Left(8).Equals("https://")
996           || m_strPath.Left(7).Equals("upnp://")
997           || m_strPath.Left(6).Equals("mms://"))
998     {
999       CFileCurl::GetContent(GetAsUrl(), m_ref);
1000
1001       // make sure there are no options set in content type
1002       // content type can look like "video/x-ms-asf ; charset=utf8"
1003       int i = m_ref.Find(';');
1004       if(i>=0)
1005         m_ref.Delete(i,m_ref.length()-i);
1006       m_ref.Trim();
1007     }
1008
1009     // if it's still empty set to an unknown type
1010     if( m_ref.IsEmpty() )
1011       m_ref = "application/octet-stream";
1012   }
1013
1014   return m_contenttype;
1015 }
1016
1017 bool CFileItem::IsSamePath(const CFileItem *item) const
1018 {
1019   if (!item)
1020     return false;
1021
1022   if (item->m_strPath == m_strPath && item->m_lStartOffset == m_lStartOffset) return true;
1023   if (IsMusicDb() && HasMusicInfoTag())
1024   {
1025     CFileItem dbItem(m_musicInfoTag->GetURL(), false);
1026     dbItem.m_lStartOffset = m_lStartOffset;
1027     return dbItem.IsSamePath(item);
1028   }
1029   if (IsVideoDb() && HasVideoInfoTag())
1030   {
1031     CFileItem dbItem(m_videoInfoTag->m_strFileNameAndPath, false);
1032     dbItem.m_lStartOffset = m_lStartOffset;
1033     return dbItem.IsSamePath(item);
1034   }
1035   if (item->IsMusicDb() && item->HasMusicInfoTag())
1036   {
1037     CFileItem dbItem(item->m_musicInfoTag->GetURL(), false);
1038     dbItem.m_lStartOffset = item->m_lStartOffset;
1039     return IsSamePath(&dbItem);
1040   }
1041   if (item->IsVideoDb() && item->HasVideoInfoTag())
1042   {
1043     CFileItem dbItem(item->m_videoInfoTag->m_strFileNameAndPath, false);
1044     dbItem.m_lStartOffset = item->m_lStartOffset;
1045     return IsSamePath(&dbItem);
1046   }
1047   return false;
1048 }
1049
1050 /////////////////////////////////////////////////////////////////////////////////
1051 /////
1052 ///// CFileItemList
1053 /////
1054 //////////////////////////////////////////////////////////////////////////////////
1055
1056 CFileItemList::CFileItemList()
1057 {
1058   m_fastLookup = false;
1059   m_bIsFolder=true;
1060   m_bCacheToDisc=false;
1061   m_sortMethod=SORT_METHOD_NONE;
1062   m_sortOrder=SORT_ORDER_NONE;
1063   m_replaceListing = false;
1064 }
1065
1066 CFileItemList::CFileItemList(const CStdString& strPath)
1067 {
1068   m_strPath=strPath;
1069   m_fastLookup = false;
1070   m_bIsFolder=true;
1071   m_bCacheToDisc=false;
1072   m_sortMethod=SORT_METHOD_NONE;
1073   m_sortOrder=SORT_ORDER_NONE;
1074   m_replaceListing = false;
1075 }
1076
1077 CFileItemList::~CFileItemList()
1078 {
1079   Clear();
1080 }
1081
1082 CFileItem* CFileItemList::operator[] (int iItem)
1083 {
1084   return Get(iItem);
1085 }
1086
1087 const CFileItem* CFileItemList::operator[] (int iItem) const
1088 {
1089   return Get(iItem);
1090 }
1091
1092 CFileItem* CFileItemList::operator[] (const CStdString& strPath)
1093 {
1094   return Get(strPath);
1095 }
1096
1097 const CFileItem* CFileItemList::operator[] (const CStdString& strPath) const
1098 {
1099   return Get(strPath);
1100 }
1101
1102 void CFileItemList::SetFastLookup(bool fastLookup)
1103 {
1104   if (fastLookup && !m_fastLookup)
1105   { // generate the map
1106     m_map.clear();
1107     for (unsigned int i=0; i < m_items.size(); i++)
1108     {
1109       CFileItem *pItem = m_items[i];
1110       CStdString path(pItem->m_strPath); path.ToLower();
1111       m_map.insert(MAPFILEITEMSPAIR(path, pItem));
1112     }
1113   }
1114   if (!fastLookup && m_fastLookup)
1115     m_map.clear();
1116   m_fastLookup = fastLookup;
1117 }
1118
1119 bool CFileItemList::Contains(const CStdString& fileName)
1120 {
1121   // checks case insensitive
1122   CStdString checkPath(fileName); checkPath.ToLower();
1123   if (m_fastLookup)
1124     return m_map.find(checkPath) != m_map.end();
1125   // slow method...
1126   for (unsigned int i = 0; i < m_items.size(); i++)
1127   {
1128     CFileItem *pItem = m_items[i];
1129     if (pItem->m_strPath.Equals(checkPath))
1130       return true;
1131   }
1132   return false;
1133 }
1134
1135 void CFileItemList::Clear()
1136 {
1137   if (m_items.size())
1138   {
1139     IVECFILEITEMS i;
1140     i = m_items.begin();
1141     while (i != m_items.end())
1142     {
1143       CFileItem* pItem = *i;
1144       delete pItem;
1145       i = m_items.erase(i);
1146     }
1147     m_map.clear();
1148   }
1149
1150   m_sortMethod=SORT_METHOD_NONE;
1151   m_sortOrder=SORT_ORDER_NONE;
1152   m_bCacheToDisc=false;
1153   m_sortDetails.clear();
1154   m_replaceListing = false;
1155   m_content.Empty();
1156 }
1157
1158 void CFileItemList::ClearKeepPointer()
1159 {
1160   if (m_items.size())
1161   {
1162     m_items.clear();
1163     m_map.clear();
1164   }
1165
1166   m_sortMethod=SORT_METHOD_NONE;
1167   m_sortOrder=SORT_ORDER_NONE;
1168   m_bCacheToDisc=false;
1169   m_sortDetails.clear();
1170   m_replaceListing = false;
1171   m_content.Empty();
1172 }
1173
1174 void CFileItemList::Add(CFileItem* pItem)
1175 {
1176   m_items.push_back(pItem);
1177   if (m_fastLookup)
1178   {
1179     CStdString path(pItem->m_strPath); path.ToLower();
1180     m_map.insert(MAPFILEITEMSPAIR(path, pItem));
1181   }
1182 }
1183
1184 void CFileItemList::AddFront(CFileItem* pItem, int itemPosition)
1185 {
1186   if (itemPosition >= 0)
1187   {
1188     m_items.insert(m_items.begin()+itemPosition, pItem);
1189   }
1190   else
1191   {
1192     m_items.insert(m_items.begin()+(m_items.size()+itemPosition), pItem);
1193   }
1194   if (m_fastLookup)
1195   {
1196     CStdString path(pItem->m_strPath); path.ToLower();
1197     m_map.insert(MAPFILEITEMSPAIR(path, pItem));
1198   }
1199 }
1200
1201 void CFileItemList::Remove(CFileItem* pItem)
1202 {
1203   for (IVECFILEITEMS it = m_items.begin(); it != m_items.end(); ++it)
1204   {
1205     if (pItem == *it)
1206     {
1207       m_items.erase(it);
1208       if (m_fastLookup)
1209       {
1210         CStdString path(pItem->m_strPath); path.ToLower();
1211         m_map.erase(path);
1212       }
1213       break;
1214     }
1215   }
1216 }
1217
1218 void CFileItemList::Remove(int iItem)
1219 {
1220   if (iItem >= 0 && iItem < (int)Size())
1221   {
1222     CFileItem* pItem = *(m_items.begin() + iItem);
1223     if (m_fastLookup)
1224     {
1225       CStdString path(pItem->m_strPath); path.ToLower();
1226       m_map.erase(path);
1227     }
1228     delete pItem;
1229     m_items.erase(m_items.begin() + iItem);
1230   }
1231 }
1232
1233 void CFileItemList::Append(const CFileItemList& itemlist)
1234 {
1235   for (int i = 0; i < itemlist.Size(); ++i)
1236   {
1237     const CFileItem* pItem = itemlist[i];
1238     CFileItem* pNewItem = new CFileItem(*pItem);
1239     Add(pNewItem);
1240   }
1241 }
1242
1243 void CFileItemList::AppendPointer(const CFileItemList& itemlist)
1244 {
1245   for (int i = 0; i < itemlist.Size(); ++i)
1246   {
1247     CFileItem* pItem = const_cast<CFileItem*>(itemlist[i]);
1248     Add(pItem);
1249   }
1250 }
1251
1252 void CFileItemList::AssignPointer(const CFileItemList& itemlist, bool append)
1253 {
1254   if (!append)
1255     Clear();
1256   AppendPointer(itemlist);
1257   m_strPath = itemlist.m_strPath;
1258   m_sortDetails = itemlist.m_sortDetails;
1259   m_replaceListing = itemlist.m_replaceListing;
1260   m_content = itemlist.m_content;
1261 }
1262
1263 CFileItem* CFileItemList::Get(int iItem)
1264 {
1265   if (iItem > -1)
1266     return m_items[iItem];
1267
1268   return NULL;
1269 }
1270
1271 const CFileItem* CFileItemList::Get(int iItem) const
1272 {
1273   if (iItem > -1)
1274     return m_items[iItem];
1275
1276   return NULL;
1277 }
1278
1279 CFileItem* CFileItemList::Get(const CStdString& strPath)
1280 {
1281   CStdString pathToCheck(strPath); pathToCheck.ToLower();
1282   if (m_fastLookup)
1283   {
1284     IMAPFILEITEMS it=m_map.find(pathToCheck);
1285     if (it != m_map.end())
1286       return it->second;
1287
1288     return NULL;
1289   }
1290   // slow method...
1291   for (unsigned int i = 0; i < m_items.size(); i++)
1292   {
1293     CFileItem *pItem = m_items[i];
1294     if (pItem->m_strPath.Equals(pathToCheck))
1295       return pItem;
1296   }
1297
1298   return NULL;
1299 }
1300
1301 const CFileItem* CFileItemList::Get(const CStdString& strPath) const
1302 {
1303   CStdString pathToCheck(strPath); pathToCheck.ToLower();
1304   if (m_fastLookup)
1305   {
1306     map<CStdString, CFileItem*>::const_iterator it=m_map.find(pathToCheck);
1307     if (it != m_map.end())
1308       return it->second;
1309
1310     return NULL;
1311   }
1312   // slow method...
1313   for (unsigned int i = 0; i < m_items.size(); i++)
1314   {
1315     CFileItem *pItem = m_items[i];
1316     if (pItem->m_strPath.Equals(pathToCheck))
1317       return pItem;
1318   }
1319
1320   return NULL;
1321 }
1322
1323 int CFileItemList::Size() const
1324 {
1325   return (int)m_items.size();
1326 }
1327
1328 bool CFileItemList::IsEmpty() const
1329 {
1330   return (m_items.size() <= 0);
1331 }
1332
1333 void CFileItemList::Reserve(int iCount)
1334 {
1335   m_items.reserve(iCount);
1336 }
1337
1338 void CFileItemList::Sort(FILEITEMLISTCOMPARISONFUNC func)
1339 {
1340   sort(m_items.begin(), m_items.end(), func);
1341 }
1342
1343 void CFileItemList::Sort(SORT_METHOD sortMethod, SORT_ORDER sortOrder)
1344 {
1345   //  Already sorted?
1346   if (sortMethod==m_sortMethod && m_sortOrder==sortOrder)
1347     return;
1348
1349   switch (sortMethod)
1350   {
1351   case SORT_METHOD_LABEL:
1352     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::LabelAscending : SSortFileItem::LabelDescending);
1353     break;
1354   case SORT_METHOD_LABEL_IGNORE_THE:
1355     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::LabelAscendingNoThe : SSortFileItem::LabelDescendingNoThe);
1356     break;
1357   case SORT_METHOD_DATE:
1358     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::DateAscending : SSortFileItem::DateDescending);
1359     break;
1360   case SORT_METHOD_SIZE:
1361     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SizeAscending : SSortFileItem::SizeDescending);
1362     break;
1363   case SORT_METHOD_DRIVE_TYPE:
1364     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::DriveTypeAscending : SSortFileItem::DriveTypeDescending);
1365     break;
1366   case SORT_METHOD_TRACKNUM:
1367     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongTrackNumAscending : SSortFileItem::SongTrackNumDescending);
1368     break;
1369   case SORT_METHOD_EPISODE:
1370     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::EpisodeNumAscending : SSortFileItem::EpisodeNumDescending);
1371     break;
1372   case SORT_METHOD_DURATION:
1373     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongDurationAscending : SSortFileItem::SongDurationDescending);
1374     break;
1375   case SORT_METHOD_TITLE_IGNORE_THE:
1376     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongTitleAscendingNoThe : SSortFileItem::SongTitleDescendingNoThe);
1377     break;
1378   case SORT_METHOD_TITLE:
1379     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongTitleAscending : SSortFileItem::SongTitleDescending);
1380     break;
1381   case SORT_METHOD_ARTIST:
1382     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongArtistAscending : SSortFileItem::SongArtistDescending);
1383     break;
1384   case SORT_METHOD_ARTIST_IGNORE_THE:
1385     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongArtistAscendingNoThe : SSortFileItem::SongArtistDescendingNoThe);
1386     break;
1387   case SORT_METHOD_ALBUM:
1388     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongAlbumAscending : SSortFileItem::SongAlbumDescending);
1389     break;
1390   case SORT_METHOD_ALBUM_IGNORE_THE:
1391     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongAlbumAscendingNoThe : SSortFileItem::SongAlbumDescendingNoThe);
1392     break;
1393   case SORT_METHOD_GENRE:
1394     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::GenreAscending : SSortFileItem::GenreDescending);
1395     break;
1396   case SORT_METHOD_FILE:
1397     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::FileAscending : SSortFileItem::FileDescending);
1398     break;
1399   case SORT_METHOD_VIDEO_RATING:
1400     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::MovieRatingAscending : SSortFileItem::MovieRatingDescending);
1401     break;
1402   case SORT_METHOD_VIDEO_TITLE:
1403     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::MovieTitleAscending : SSortFileItem::MovieTitleDescending);
1404     break;
1405   case SORT_METHOD_VIDEO_YEAR:
1406     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::MovieYearAscending : SSortFileItem::MovieYearDescending);
1407     break;
1408   case SORT_METHOD_PRODUCTIONCODE:
1409     Sort(sortOrder==SORT_ORDER_ASC? SSortFileItem::ProductionCodeAscending : SSortFileItem::ProductionCodeDescending);
1410     break;
1411   case SORT_METHOD_PROGRAM_COUNT:
1412   case SORT_METHOD_PLAYLIST_ORDER:
1413     // TODO: Playlist order is hacked into program count variable (not nice, but ok until 2.0)
1414     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::ProgramCountAscending : SSortFileItem::ProgramCountDescending);
1415     break;
1416   case SORT_METHOD_SONG_RATING:
1417     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::SongRatingAscending : SSortFileItem::SongRatingDescending);
1418     break;
1419   case SORT_METHOD_MPAA_RATING:
1420     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::MPAARatingAscending : SSortFileItem::MPAARatingDescending);
1421     break;
1422   case SORT_METHOD_VIDEO_RUNTIME:
1423     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::MovieRuntimeAscending : SSortFileItem::MovieRuntimeDescending);
1424     break;
1425   case SORT_METHOD_STUDIO:
1426     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::StudioAscending : SSortFileItem::StudioDescending);
1427     break;
1428   case SORT_METHOD_STUDIO_IGNORE_THE:
1429     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::StudioAscendingNoThe : SSortFileItem::StudioDescendingNoThe);
1430     break;
1431   default:
1432     break;
1433   }
1434
1435   m_sortMethod=sortMethod;
1436   m_sortOrder=sortOrder;
1437 }
1438
1439 void CFileItemList::Randomize()
1440 {
1441   random_shuffle(m_items.begin(), m_items.end());
1442 }
1443
1444 void CFileItemList::Serialize(CArchive& ar)
1445 {
1446   if (ar.IsStoring())
1447   {
1448     CFileItem::Serialize(ar);
1449
1450     int i = 0;
1451     if (m_items.size() > 0 && m_items[0]->IsParentFolder())
1452       i = 1;
1453
1454     ar << (int)(m_items.size() - i);
1455
1456     ar << m_fastLookup;
1457
1458     ar << (int)m_sortMethod;
1459     ar << (int)m_sortOrder;
1460     ar << m_bCacheToDisc;
1461
1462     ar << (int)m_sortDetails.size();
1463     for (unsigned int j = 0; j < m_sortDetails.size(); ++j)
1464     {
1465       const SORT_METHOD_DETAILS &details = m_sortDetails[j];
1466       ar << (int)details.m_sortMethod;
1467       ar << details.m_buttonLabel;
1468       ar << details.m_labelMasks.m_strLabelFile;
1469       ar << details.m_labelMasks.m_strLabelFolder;
1470       ar << details.m_labelMasks.m_strLabel2File;
1471       ar << details.m_labelMasks.m_strLabel2Folder;
1472     }
1473
1474     ar << m_content;
1475
1476     for (i; i < (int)m_items.size(); ++i)
1477     {
1478       CFileItem* pItem = m_items[i];
1479       ar << *pItem;
1480     }
1481   }
1482   else
1483   {
1484     CFileItem* pParent=NULL;
1485     if (!IsEmpty())
1486     {
1487       CFileItem* pItem=m_items[0];
1488       if (pItem->IsParentFolder())
1489         pParent = new CFileItem(*pItem);
1490     }
1491
1492     SetFastLookup(false);
1493     Clear();
1494
1495
1496     CFileItem::Serialize(ar);
1497
1498     int iSize = 0;
1499     ar >> iSize;
1500     if (iSize <= 0)
1501       return ;
1502
1503     if (pParent)
1504     {
1505       m_items.reserve(iSize + 1);
1506       m_items.push_back(pParent);
1507     }
1508     else
1509       m_items.reserve(iSize);
1510
1511     bool fastLookup=false;
1512     ar >> fastLookup;
1513
1514     ar >> (int&)m_sortMethod;
1515     ar >> (int&)m_sortOrder;
1516     ar >> m_bCacheToDisc;
1517
1518     unsigned int detailSize = 0;
1519     ar >> detailSize;
1520     for (unsigned int j = 0; j < detailSize; ++j)
1521     {
1522       SORT_METHOD_DETAILS details;
1523       ar >> (int&)details.m_sortMethod;
1524       ar >> details.m_buttonLabel;
1525       ar >> details.m_labelMasks.m_strLabelFile;
1526       ar >> details.m_labelMasks.m_strLabelFolder;
1527       ar >> details.m_labelMasks.m_strLabel2File;
1528       ar >> details.m_labelMasks.m_strLabel2Folder;
1529       m_sortDetails.push_back(details);
1530     }
1531
1532     ar >> m_content;
1533
1534     for (int i = 0; i < iSize; ++i)
1535     {
1536       CFileItem* pItem = new CFileItem;
1537       ar >> *pItem;
1538       Add(pItem);
1539     }
1540
1541     SetFastLookup(fastLookup);
1542   }
1543 }
1544
1545 void CFileItemList::FillInDefaultIcons()
1546 {
1547   for (int i = 0; i < (int)m_items.size(); ++i)
1548   {
1549     CFileItem* pItem = m_items[i];
1550     pItem->FillInDefaultIcon();
1551   }
1552 }
1553
1554 void CFileItemList::SetMusicThumbs()
1555 {
1556   //cache thumbnails directory
1557   g_directoryCache.InitMusicThumbCache();
1558
1559   for (int i = 0; i < (int)m_items.size(); ++i)
1560   {
1561     CFileItem* pItem = m_items[i];
1562     pItem->SetMusicThumb();
1563   }
1564
1565   g_directoryCache.ClearMusicThumbCache();
1566 }
1567
1568 int CFileItemList::GetFolderCount() const
1569 {
1570   int nFolderCount = 0;
1571   for (int i = 0; i < (int)m_items.size(); i++)
1572   {
1573     CFileItem* pItem = m_items[i];
1574     if (pItem->m_bIsFolder)
1575       nFolderCount++;
1576   }
1577
1578   return nFolderCount;
1579 }
1580
1581 int CFileItemList::GetFileCount() const
1582 {
1583   int nFileCount = 0;
1584   for (int i = 0; i < (int)m_items.size(); i++)
1585   {
1586     CFileItem* pItem = m_items[i];
1587     if (!pItem->m_bIsFolder)
1588       nFileCount++;
1589   }
1590
1591   return nFileCount;
1592 }
1593
1594 int CFileItemList::GetSelectedCount() const
1595 {
1596   int count = 0;
1597   for (int i = 0; i < (int)m_items.size(); i++)
1598   {
1599     CFileItem* pItem = m_items[i];
1600     if (pItem->IsSelected())
1601       count++;
1602   }
1603
1604   return count;
1605 }
1606
1607 void CFileItemList::FilterCueItems()
1608 {
1609   // Handle .CUE sheet files...
1610   VECSONGS itemstoadd;
1611   CStdStringArray itemstodelete;
1612   for (int i = 0; i < (int)m_items.size(); i++)
1613   {
1614     CFileItem *pItem = m_items[i];
1615     if (!pItem->m_bIsFolder)
1616     { // see if it's a .CUE sheet
1617       if (pItem->IsCUESheet())
1618       {
1619         CCueDocument cuesheet;
1620         if (cuesheet.Parse(pItem->m_strPath))
1621         {
1622           VECSONGS newitems;
1623           cuesheet.GetSongs(newitems);
1624
1625           std::vector<CStdString> MediaFileVec;
1626           cuesheet.GetMediaFiles(MediaFileVec);
1627
1628           // queue the cue sheet and the underlying media file for deletion
1629           for(std::vector<CStdString>::iterator itMedia = MediaFileVec.begin(); itMedia != MediaFileVec.end(); itMedia++)
1630           {
1631             CStdString strMediaFile = *itMedia;
1632             bool bFoundMediaFile = CFile::Exists(strMediaFile);
1633             // queue the cue sheet and the underlying media file for deletion
1634             if (!bFoundMediaFile)
1635             {
1636               // try file in same dir, not matching case...
1637               if (Contains(strMediaFile))
1638               {
1639                 bFoundMediaFile = true;
1640               }
1641               else
1642               {
1643                 // try removing the .cue extension...
1644                 strMediaFile = pItem->m_strPath;
1645                 CUtil::RemoveExtension(strMediaFile);
1646                 CFileItem item(strMediaFile, false);
1647                 if (item.IsAudio() && Contains(strMediaFile))
1648                 {
1649                   bFoundMediaFile = true;
1650                 }
1651                 else
1652                 { // try replacing the extension with one of our allowed ones.
1653                   CStdStringArray extensions;
1654                   StringUtils::SplitString(g_stSettings.m_musicExtensions, "|", extensions);
1655                   for (unsigned int i = 0; i < extensions.size(); i++)
1656                   {
1657                     CUtil::ReplaceExtension(pItem->m_strPath, extensions[i], strMediaFile);
1658                     CFileItem item(strMediaFile, false);
1659                     if (!item.IsCUESheet() && !item.IsPlayList() && Contains(strMediaFile))
1660                     {
1661                       bFoundMediaFile = true;
1662                       break;
1663                     }
1664                   }
1665                 }
1666               }
1667             }
1668             if (bFoundMediaFile)
1669             {
1670               itemstodelete.push_back(pItem->m_strPath);
1671               itemstodelete.push_back(strMediaFile);
1672               // get the additional stuff (year, genre etc.) from the underlying media files tag.
1673               CMusicInfoTag tag;
1674               auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(strMediaFile));
1675               if (NULL != pLoader.get())
1676               {
1677                 // get id3tag
1678                 pLoader->Load(strMediaFile, tag);
1679               }
1680               // fill in any missing entries from underlying media file
1681               for (int j = 0; j < (int)newitems.size(); j++)
1682               {
1683                 CSong song = newitems[j];
1684                 // only for songs that actually match the current media file
1685                 if (song.strFileName == strMediaFile)
1686                 {
1687                   if (tag.Loaded())
1688                   {
1689                     if (song.strAlbum.empty() && !tag.GetAlbum().empty()) song.strAlbum = tag.GetAlbum();
1690                     if (song.strGenre.empty() && !tag.GetGenre().empty()) song.strGenre = tag.GetGenre();
1691                     if (song.strArtist.empty() && !tag.GetArtist().empty()) song.strArtist = tag.GetArtist();
1692                     SYSTEMTIME dateTime;
1693                     tag.GetReleaseDate(dateTime);
1694                     if (dateTime.wYear) song.iYear = dateTime.wYear;
1695                   }
1696                   if (!song.iDuration && tag.GetDuration() > 0)
1697                   { // must be the last song
1698                     song.iDuration = (tag.GetDuration() * 75 - song.iStartOffset + 37) / 75;
1699                   }
1700                   // add this item to the list
1701                   itemstoadd.push_back(song);
1702                 }
1703               }
1704             }
1705             else
1706             { // remove the .cue sheet from the directory
1707               itemstodelete.push_back(pItem->m_strPath);
1708             }
1709           }
1710         }
1711         else
1712         { // remove the .cue sheet from the directory (can't parse it - no point listing it)
1713           itemstodelete.push_back(pItem->m_strPath);
1714         }
1715       }
1716     }
1717   }
1718   // now delete the .CUE files and underlying media files.
1719   for (int i = 0; i < (int)itemstodelete.size(); i++)
1720   {
1721     for (int j = 0; j < (int)m_items.size(); j++)
1722     {
1723       CFileItem *pItem = m_items[j];
1724       if (stricmp(pItem->m_strPath.c_str(), itemstodelete[i].c_str()) == 0)
1725       { // delete this item
1726         delete pItem;
1727         m_items.erase(m_items.begin() + j);
1728         break;
1729       }
1730     }
1731   }
1732   // and add the files from the .CUE sheet
1733   for (int i = 0; i < (int)itemstoadd.size(); i++)
1734   {
1735     // now create the file item, and add to the item list.
1736     CFileItem *pItem = new CFileItem(itemstoadd[i]);
1737     m_items.push_back(pItem);
1738   }
1739 }
1740
1741 // Remove the extensions from the filenames
1742 void CFileItemList::RemoveExtensions()
1743 {
1744   for (int i = 0; i < Size(); ++i)
1745     m_items[i]->RemoveExtension();
1746 }
1747
1748 void CFileItemList::CleanFileNames()
1749 {
1750   for (int i = 0; i < Size(); ++i)
1751     m_items[i]->CleanFileName();
1752 }
1753
1754 void CFileItemList::Stack()
1755 {
1756   // TODO: Remove nfo files before this stage?  The old routine did, but I'm not sure
1757   // the advantage of this (seems to me it's better just to ignore them for stacking
1758   // purposes).
1759   if (g_stSettings.m_iMyVideoStack != STACK_NONE)
1760   {
1761     // items needs to be sorted for stuff below to work properly
1762     Sort(SORT_METHOD_LABEL, SORT_ORDER_ASC);
1763
1764     // First stack any DVD folders by removing every dvd file other than
1765     // the VIDEO_TS.IFO file.
1766     bool isDVDFolder(false);
1767     for (int i = 0; i < Size(); ++i)
1768     {
1769       CFileItem *item = Get(i);
1770       if (item->GetLabel().CompareNoCase("video_ts.ifo") == 0)
1771       {
1772         isDVDFolder = true;
1773         break;
1774       }
1775       if (item->m_bIsFolder)
1776       { // we stack CD# folders down if they contain a single video file
1777         CStdString folderName = item->GetLabel();
1778         if (folderName.Left(2).Equals("CD") && StringUtils::IsNaturalNumber(folderName.Mid(2)))
1779         {
1780           CFileItemList items;
1781           CDirectory::GetDirectory(item->m_strPath,items,g_stSettings.m_videoExtensions,true);
1782           if (items.GetFileCount() == 1)  
1783           { // only one file in the list, so replace our item with this one
1784             for (int j = 0; j < items.Size(); j++)
1785             {
1786               if (!items[j]->m_bIsFolder)
1787               {
1788                 *item = *items[j];
1789                 break;
1790               }
1791             }
1792           }
1793         }
1794       }
1795     }
1796     if (isDVDFolder)
1797     { // remove any other ifo files in this folder
1798       // leave the vobs stacked.
1799       const int num = Size() ? Size() - 1 : 0;  // Size will alter in this loop
1800       for (int i = num; i; --i)
1801       {
1802         CFileItem *item = Get(i);
1803         if (item->IsDVDFile(false, true) && item->GetLabel().CompareNoCase("video_ts.ifo"))
1804         {
1805           Remove(i);
1806         }
1807       }
1808     }
1809     // Stacking
1810     for (int i = 0; i < Size(); ++i)
1811     {
1812       CFileItem *item = Get(i);
1813
1814       // ignore parent directories, playlists and the virtual root
1815       if (item->IsPlayList() || item->IsParentFolder() || item->IsNFO() || IsVirtualDirectoryRoot() || item->IsDVDImage())
1816         continue;
1817
1818       if( item->m_bIsFolder)
1819       {
1820         // check for any dvd directories, only on known fast types
1821         // i'm adding xbms even thou it really isn't fast due to
1822         // opening file to check for existance
1823         if( !item->IsRemote()
1824          || item->IsSmb()
1825          || CUtil::IsInRAR(item->m_strPath)
1826          || CUtil::IsInZIP(item->m_strPath)
1827          || item->m_strPath.Left(5).Equals("xbms", false)
1828          )
1829         {
1830           CStdString path;
1831           CUtil::AddFileToFolder(item->m_strPath, "VIDEO_TS.IFO", path);
1832           if (CFile::Exists(path))
1833           {
1834             /* set the thumbnail based on folder */
1835             item->SetCachedVideoThumb();
1836             if (!item->HasThumbnail())
1837               item->SetUserVideoThumb();
1838
1839             item->m_bIsFolder = false;
1840             item->m_strPath = path;
1841             item->SetLabel2("");
1842             item->SetLabelPreformated(true);
1843             m_sortMethod = SORT_METHOD_NONE; /* sorting is now broken */
1844
1845             /* override the previously set thumb if video_ts.ifo has any */
1846             /* otherwise user can't set icon on the stacked file as that */
1847             /* will allways be set on the video_ts.ifo file */
1848             CStdString thumb(item->GetCachedVideoThumb());
1849             if(CFile::Exists(thumb))
1850               item->SetThumbnailImage(thumb);
1851             else
1852               item->SetUserVideoThumb();
1853
1854           }
1855         }
1856         continue;
1857       }
1858
1859       CStdString fileName, filePath;
1860       CUtil::Split(item->m_strPath, filePath, fileName);
1861       CStdString fileTitle, volumeNumber;
1862       //CLog::Log(LOGDEBUG,"Trying to stack: %s", item->GetLabel().c_str());
1863       if (CUtil::GetVolumeFromFileName(item->GetLabel(), fileTitle, volumeNumber))
1864       {
1865         vector<int> stack;
1866         stack.push_back(i);
1867         __int64 size = item->m_dwSize;
1868         for (int j = i + 1; j < Size(); ++j)
1869         {
1870           CFileItem *item2 = Get(j);
1871           // ignore directories, nfo files and playlists
1872           if (item2->IsPlayList() || item2->m_bIsFolder || item2->IsNFO())
1873             continue;
1874           CStdString fileName2, filePath2;
1875           CUtil::Split(item2->m_strPath, filePath2, fileName2);
1876           CStdString fileTitle2, volumeNumber2;
1877           //if (CUtil::GetVolumeFromFileName(fileName2, fileTitle2, volumeNumber2))
1878           if (CUtil::GetVolumeFromFileName(Get(j)->GetLabel(), fileTitle2, volumeNumber2))
1879           {
1880             if (fileTitle2.Equals(fileTitle, false))
1881             {
1882               //CLog::Log(LOGDEBUG,"  adding item: [%03i] %s", j, Get(j)->GetLabel().c_str());
1883               stack.push_back(j);
1884               size += item2->m_dwSize;
1885             }
1886           }
1887         }
1888         if (stack.size() > 1)
1889         {
1890           // have a stack, remove the items and add the stacked item
1891           CStackDirectory dir;
1892           // dont actually stack a multipart rar set, just remove all items but the first
1893           CStdString stackPath;
1894           if (Get(stack[0])->IsRAR())
1895             stackPath = Get(stack[0])->m_strPath;
1896           else
1897             stackPath = dir.ConstructStackPath(*this, stack);
1898           for (unsigned int j = stack.size() - 1; j > 0; --j)
1899             Remove(stack[j]);
1900           item->m_strPath = stackPath;
1901           // item->m_bIsFolder = true;  // don't treat stacked files as folders
1902           // the label may be in a different char set from the filename (eg over smb
1903           // the label is converted from utf8, but the filename is not)
1904           CUtil::GetVolumeFromFileName(item->GetLabel(), fileTitle, volumeNumber);
1905           item->SetLabel(fileTitle);
1906           item->m_dwSize = size;
1907           //CLog::Log(LOGDEBUG,"  ** finalized stack: %s", fileTitle.c_str());
1908         }
1909       }
1910     }
1911   }
1912 }
1913
1914 bool CFileItemList::Load()
1915 {
1916   CFile file;
1917   if (file.Open(GetDiscCacheFile()))
1918   {
1919     CLog::Log(LOGDEBUG,"Loading fileitems [%s]",m_strPath.c_str());
1920     CArchive ar(&file, CArchive::load);
1921     ar >> *this;
1922     CLog::Log(LOGDEBUG,"  -- items: %i, directory: %s sort method: %i, ascending: %s",Size(),m_strPath.c_str(), m_sortMethod, m_sortOrder ? "true" : "false");
1923     ar.Close();
1924     file.Close();
1925     return true;
1926   }
1927
1928   return false;
1929 }
1930
1931 bool CFileItemList::Save()
1932 {
1933   int iSize = Size();
1934   if (iSize <= 0)
1935     return false;
1936
1937   CLog::Log(LOGDEBUG,"Saving fileitems [%s]",m_strPath.c_str());
1938
1939   CFile file;
1940   if (file.OpenForWrite(GetDiscCacheFile(), true, true)) // overwrite always
1941   {
1942     CArchive ar(&file, CArchive::store);
1943     ar << *this;
1944     CLog::Log(LOGDEBUG,"  -- items: %i, sort method: %i, ascending: %s",iSize,m_sortMethod, m_sortOrder ? "true" : "false");
1945     ar.Close();
1946     file.Close();
1947     return true;
1948   }
1949
1950   return false;
1951 }
1952
1953 void CFileItemList::RemoveDiscCache()
1954 {
1955   CLog::Log(LOGDEBUG,"Clearing cached fileitems [%s]",m_strPath.c_str());
1956   if (CFile::Exists(GetDiscCacheFile()))
1957     CFile::Delete(GetDiscCacheFile());
1958 }
1959
1960 CStdString CFileItemList::GetDiscCacheFile()
1961 {
1962   CStdString strPath=m_strPath;
1963   CUtil::RemoveSlashAtEnd(strPath);
1964
1965   Crc32 crc;
1966   crc.ComputeFromLowerCase(strPath);
1967
1968   CStdString cacheFile;
1969   if (IsCDDA() || IsOnDVD())
1970     cacheFile.Format("Z:\\r-%08x.fi", (unsigned __int32)crc);
1971   else if (IsMusicDb())
1972     cacheFile.Format("Z:\\mdb-%08x.fi", (unsigned __int32)crc);
1973   else if (IsVideoDb())
1974     cacheFile.Format("Z:\\vdb-%08x.fi", (unsigned __int32)crc);
1975   else
1976     cacheFile.Format("Z:\\%08x.fi", (unsigned __int32)crc);
1977   return cacheFile;
1978 }
1979
1980 bool CFileItemList::AlwaysCache()
1981 {
1982   // some database folders are always cached
1983   if (IsMusicDb())
1984     return CMusicDatabaseDirectory::CanCache(m_strPath);
1985   if (IsVideoDb())
1986     return CVideoDatabaseDirectory::CanCache(m_strPath);
1987   return false;
1988 }
1989
1990 void CFileItemList::SetCachedVideoThumbs()
1991 {
1992   // TODO: Investigate caching time to see if it speeds things up
1993   for (unsigned int i = 0; i < m_items.size(); ++i)
1994   {
1995     CFileItem* pItem = m_items[i];
1996     pItem->SetCachedVideoThumb();
1997   }
1998 }
1999
2000 void CFileItemList::SetCachedProgramThumbs()
2001 {
2002   // TODO: Investigate caching time to see if it speeds things up
2003   for (unsigned int i = 0; i < m_items.size(); ++i)
2004   {
2005     CFileItem* pItem = m_items[i];
2006     pItem->SetCachedProgramThumb();
2007   }
2008 }
2009
2010 void CFileItemList::SetCachedMusicThumbs()
2011 {
2012   // TODO: Investigate caching time to see if it speeds things up
2013   for (unsigned int i = 0; i < m_items.size(); ++i)
2014   {
2015     CFileItem* pItem = m_items[i];
2016     pItem->SetCachedMusicThumb();
2017   }
2018 }
2019
2020 CStdString CFileItem::GetCachedPictureThumb()
2021 {
2022   // get the locally cached thumb
2023   Crc32 crc;
2024   crc.ComputeFromLowerCase(m_strPath);
2025   CStdString hex;
2026   hex.Format("%08x", (unsigned __int32) crc);
2027   CStdString thumb;
2028   thumb.Format("%s\\%c\\%s.tbn", g_settings.GetPicturesThumbFolder().c_str(), hex[0], hex.c_str());
2029   return thumb;
2030 }
2031
2032 void CFileItem::SetCachedMusicThumb()
2033 {
2034   // if it already has a thumbnail, then return
2035   if (HasThumbnail() || m_bIsShareOrDrive) return ;
2036
2037   // streams do not have thumbnails
2038   if (IsInternetStream()) return ;
2039
2040   //  music db items already have thumbs or there is no thumb available
2041   if (IsMusicDb()) return;
2042
2043   // ignore the parent dir items
2044   if (IsParentFolder()) return;
2045
2046   CStdString cachedThumb(GetPreviouslyCachedMusicThumb());
2047   if (!cachedThumb.IsEmpty())
2048     SetThumbnailImage(cachedThumb);
2049     // SetIconImage(cachedThumb);
2050 }
2051
2052 CStdString CFileItem::GetPreviouslyCachedMusicThumb()
2053 {
2054   // the highest priority thumb is album name + album path
2055   CStdString strPath;
2056   if (!m_bIsFolder)
2057     CUtil::GetDirectory(m_strPath, strPath);
2058   else
2059     strPath = m_strPath;
2060   // music thumbs are cached without slash at end
2061   CUtil::RemoveSlashAtEnd(strPath);
2062
2063   // look if an album thumb is available,
2064   // could be any file with tags loaded or
2065   // a directory in album window
2066   CStdString strAlbum, strArtist;
2067   if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
2068   {
2069     strAlbum = m_musicInfoTag->GetAlbum();
2070     if (!m_musicInfoTag->GetAlbumArtist().IsEmpty())
2071       strArtist = m_musicInfoTag->GetAlbumArtist();
2072     else
2073       strArtist = m_musicInfoTag->GetArtist();
2074   }
2075   if (!strAlbum.IsEmpty() && !strArtist.IsEmpty())
2076   {
2077     // try permanent album thumb (Q:\userdata\thumbnails\music)
2078     // using "album name + artist name"
2079     CStdString thumb(CUtil::GetCachedAlbumThumb(strAlbum, strArtist));
2080     if (CFile::Exists(thumb))
2081       return thumb;
2082   }
2083
2084   // if a file, try to find a cached filename.tbn
2085   if (!m_bIsFolder)
2086   {
2087     // look for locally cached tbn
2088     CStdString thumb(CUtil::GetCachedMusicThumb(m_strPath));
2089     if (CFile::Exists(thumb))
2090       return thumb;
2091   }
2092
2093   // try and find a cached folder thumb (folder.jpg or folder.tbn)
2094   CStdString thumb(CUtil::GetCachedMusicThumb(strPath));
2095   if (CFile::Exists(thumb))
2096     return thumb;
2097
2098   return "";
2099 }
2100
2101 CStdString CFileItem::GetUserMusicThumb(bool alwaysCheckRemote /* = false */)
2102 {
2103   if (m_bIsShareOrDrive) return "";
2104   if (IsInternetStream()) return "";
2105   if (IsParentFolder()) return "";
2106   if (IsMusicDb()) return "";
2107   CURL url(m_strPath);
2108   if (url.GetProtocol() == "rar" || url.GetProtocol() == "zip") return "";
2109   if (url.GetProtocol() == "upnp" || url.GetProtocol() == "ftp" || url.GetProtocol() == "ftps") return "";
2110
2111   // we first check for <filename>.tbn or <foldername>.tbn
2112   CStdString fileThumb(GetTBNFile());
2113   if (CFile::Exists(fileThumb))
2114     return fileThumb;
2115   // if a folder, check for folder.jpg
2116   if (m_bIsFolder && (!IsRemote() || alwaysCheckRemote || g_guiSettings.GetBool("musicfiles.findremotethumbs")))
2117   {
2118     CStdStringArray thumbs;
2119     StringUtils::SplitString(g_advancedSettings.m_musicThumbs, "|", thumbs);
2120     for (unsigned int i = 0; i < thumbs.size(); ++i)
2121     {
2122       CStdString folderThumb(GetFolderThumb(thumbs[i]));
2123       if (CFile::Exists(folderThumb))
2124       {
2125         return folderThumb;
2126       }
2127     }
2128   }
2129   // this adds support for files which inherit a folder.jpg icon which has not been cached yet.
2130   // this occurs when queueing a top-level folder which has not been traversed yet.
2131   else if (!IsRemote() || alwaysCheckRemote || g_guiSettings.GetBool("musicfiles.findremotethumbs"))
2132   {
2133     CStdString strFolder, strFile;
2134     CUtil::Split(m_strPath, strFolder, strFile);
2135     CFileItem folderItem(strFolder, true);
2136     folderItem.SetMusicThumb(alwaysCheckRemote);
2137     if (folderItem.HasThumbnail())
2138       return folderItem.GetThumbnailImage();
2139   }
2140   // No thumb found
2141   return "";
2142 }
2143
2144 void CFileItem::SetUserMusicThumb(bool alwaysCheckRemote /* = false */)
2145 {
2146   // caches as the local thumb
2147   CStdString thumb(GetUserMusicThumb(alwaysCheckRemote));
2148   if (!thumb.IsEmpty())
2149   {
2150     CStdString cachedThumb(CUtil::GetCachedMusicThumb(m_strPath));
2151     CPicture pic;
2152     pic.DoCreateThumbnail(thumb, cachedThumb);
2153   }
2154
2155   SetCachedMusicThumb();
2156 }
2157
2158 void CFileItem::SetCachedPictureThumb()
2159 {
2160   if (IsParentFolder()) return;
2161   CStdString cachedThumb(GetCachedPictureThumb());
2162   if (CFile::Exists(cachedThumb))
2163     SetThumbnailImage(cachedThumb);
2164 }
2165
2166 CStdString CFileItem::GetCachedVideoThumb()
2167 {
2168   // get the locally cached thumb
2169   Crc32 crc;
2170   if (IsStack())
2171   {
2172     CStackDirectory dir;
2173     crc.ComputeFromLowerCase(dir.GetFirstStackedFile(m_strPath));
2174   }
2175   else
2176     crc.ComputeFromLowerCase(m_strPath);
2177
2178   CStdString hex;
2179   hex.Format("%08x", (__int32)crc);
2180   CStdString thumb;
2181   thumb.Format("%s\\%c\\%08x.tbn", g_settings.GetVideoThumbFolder().c_str(), hex[0],(unsigned __int32)crc);
2182   return thumb;
2183 }
2184
2185 void CFileItem::SetCachedVideoThumb()
2186 {
2187   if (IsParentFolder()) return;
2188   CStdString cachedThumb(GetCachedVideoThumb());
2189   if (CFile::Exists(cachedThumb))
2190     SetThumbnailImage(cachedThumb);
2191 }
2192
2193 // Gets the .tbn filename from a file or folder name.
2194 // <filename>.ext -> <filename>.tbn
2195 // <foldername>/ -> <foldername>.tbn
2196 CStdString CFileItem::GetTBNFile()
2197 {
2198   // special case for zip/rar
2199   if (IsRAR() || IsZIP())
2200   {
2201     // extract the filename portion and find the tbn based on that
2202     CURL url(m_strPath);
2203     CFileItem item(url.GetFileName());
2204     url.SetFileName(item.GetTBNFile());
2205     CStdString thumbFile;
2206     url.GetURL(thumbFile);
2207     return thumbFile;
2208   }
2209   if (m_bIsFolder && !IsFileFolder())
2210   {
2211     CStdString thumbFile(m_strPath);
2212     CUtil::RemoveSlashAtEnd(thumbFile);
2213     return thumbFile + ".tbn";
2214   }
2215   else
2216   {
2217     CStdString thumbFile;
2218     CUtil::ReplaceExtension(m_strPath, ".tbn", thumbFile);
2219     return thumbFile;
2220   }
2221 }
2222
2223 CStdString CFileItem::GetUserVideoThumb()
2224 {
2225   if (m_bIsShareOrDrive) return "";
2226   if (IsInternetStream()) return "";
2227   if (IsParentFolder()) return "";
2228   CURL url(m_strPath);
2229   if (url.GetProtocol() == "rar" || url.GetProtocol() == "zip") return "";
2230   if (url.GetProtocol() == "upnp" || url.GetProtocol() == "ftp") return "";
2231   if (url.GetProtocol() == "tuxbox")
2232   {
2233     if (!m_bIsFolder)
2234       return g_tuxbox.GetPicon(GetLabel());
2235     else return "";
2236   }
2237
2238   // 1. check <filename>.tbn or <foldername>.tbn
2239   CStdString fileThumb;
2240   if (IsStack())
2241   {
2242     CStackDirectory dir;
2243     CFileItem item(dir.GetFirstStackedFile(m_strPath), false);
2244     fileThumb = item.GetTBNFile();
2245   }
2246   else
2247     fileThumb = GetTBNFile();
2248   if (CFile::Exists(fileThumb))
2249     return fileThumb;
2250   // 2. if a folder, check for folder.jpg
2251   if (m_bIsFolder)
2252   {
2253     CStdString folderThumb(GetFolderThumb());
2254     if (CFile::Exists(folderThumb))
2255       return folderThumb;
2256   }
2257   // No thumb found
2258   return "";
2259 }
2260
2261 CStdString CFileItem::GetFolderThumb(const CStdString &folderJPG /* = "folder.jpg" */) const
2262 {
2263   CStdString folderThumb;
2264   if (IsMultiPath())
2265     CUtil::AddFileToFolder(CMultiPathDirectory::GetFirstPath(m_strPath), folderJPG, folderThumb);
2266   else
2267     CUtil::AddFileToFolder(m_strPath, folderJPG, folderThumb);
2268   return folderThumb;
2269 }
2270
2271 void CFileItem::SetVideoThumb()
2272 {
2273   if (HasThumbnail()) return;
2274   SetCachedVideoThumb();
2275   if (!HasThumbnail())
2276     SetUserVideoThumb();
2277 }
2278
2279 void CFileItem::SetUserVideoThumb()
2280 {
2281   if (m_bIsShareOrDrive) return;
2282   if (IsParentFolder()) return;
2283
2284   // caches as the local thumb
2285   CStdString thumb(GetUserVideoThumb());
2286   if (!thumb.IsEmpty())
2287   {
2288     CStdString cachedThumb(GetCachedVideoThumb());
2289     CPicture pic;
2290     pic.DoCreateThumbnail(thumb, cachedThumb);
2291   }
2292   SetCachedVideoThumb();
2293 }
2294
2295 ///
2296 /// If a cached fanart image already exists, then we're fine.  Otherwise, we look for a local fanart.jpg
2297 /// and cache that image as our fanart.
2298 void CFileItem::CacheVideoFanart()
2299 {
2300   CStdString cachedFanart(GetCachedVideoFanart());
2301   // First check for an already cached fanart image
2302   if (CFile::Exists(cachedFanart))
2303     return;
2304   // We don't have a cached image, so let's see if the user has a local image they want to use
2305   CStdString folderFanart(GetFolderThumb("fanart.png"));
2306   if (!CFile::Exists(folderFanart))
2307   {
2308     folderFanart = GetFolderThumb("fanart.jpg");
2309     if (!CFile::Exists(folderFanart))
2310       return;
2311   }
2312   CPicture pic;
2313   pic.CacheImage(folderFanart, cachedFanart);
2314 }
2315
2316 CStdString CFileItem::GetCachedVideoFanart()
2317 {
2318   // get the locally cached thumb
2319   Crc32 crc;
2320   if (IsStack())
2321   {
2322     CStackDirectory dir;
2323     crc.ComputeFromLowerCase(dir.GetFirstStackedFile(m_strPath));
2324   }
2325   else
2326     crc.ComputeFromLowerCase(m_strPath);
2327
2328   CStdString thumb;
2329   thumb.Format("%s\\%08x.tbn", g_settings.GetVideoFanartFolder().c_str(),(unsigned __int32)crc);
2330   return thumb;
2331 }
2332
2333 CStdString CFileItem::GetCachedProgramThumb()
2334 {
2335   // get the locally cached thumb
2336   Crc32 crc;
2337   if (IsOnDVD())
2338   {
2339     CStdString strDesc;
2340     CUtil::GetXBEDescription(m_strPath,strDesc);
2341     CStdString strCRC;
2342     strCRC.Format("%s%u",strDesc.c_str(),CUtil::GetXbeID(m_strPath));
2343     crc.ComputeFromLowerCase(strCRC);
2344   }
2345   else
2346     crc.ComputeFromLowerCase(m_strPath);
2347   CStdString thumb;
2348   thumb.Format("%s\\%08x.tbn", g_settings.GetProgramsThumbFolder().c_str(), (unsigned __int32)crc);
2349   return thumb;
2350 }
2351
2352 CStdString CFileItem::GetCachedGameSaveThumb()
2353 {
2354   CStdString extension;
2355   CUtil::GetExtension(m_strPath,extension);
2356   if (extension.Equals(".xbx")) // savemeta.xbx - cache thumb
2357   {
2358     Crc32 crc;
2359     crc.ComputeFromLowerCase(m_strPath);
2360     CStdString thumb;
2361     thumb.Format("%s\\%08x.tbn", g_settings.GetGameSaveThumbFolder().c_str(),(unsigned __int32)crc);
2362     if (!CFile::Exists(thumb))
2363     {
2364       CStdString strTitleImage, strParent, strParentSave, strParentTitle;
2365       CUtil::GetDirectory(m_strPath,strTitleImage);
2366       CUtil::GetParentPath(strTitleImage,strParent);
2367       CUtil::AddFileToFolder(strTitleImage,"saveimage.xbx",strTitleImage);
2368       CUtil::AddFileToFolder(strParent,"saveimage.xbx",strParentSave);
2369       CUtil::AddFileToFolder(strParent,"titleimage.xbx",strParentTitle);
2370       //CUtil::AddFileToFolder(strTitleImageCur,"titleimage.xbx",m_strPath);
2371       if (CFile::Exists(strTitleImage))
2372         CUtil::CacheXBEIcon(strTitleImage, thumb);
2373       else if (CFile::Exists(strParentSave))
2374         CUtil::CacheXBEIcon(strParentSave,thumb);
2375       else if (CFile::Exists(strParentTitle))
2376         CUtil::CacheXBEIcon(strParentTitle,thumb);
2377       else
2378         thumb = "";
2379     }
2380     return thumb;
2381   }
2382   else if (CDirectory::Exists(m_strPath))
2383   {
2384     // get the save game id
2385     CStdString fullPath(m_strPath);
2386     CUtil::RemoveSlashAtEnd(fullPath);
2387     CStdString fileName(CUtil::GetFileName(fullPath));
2388
2389     CStdString thumb;
2390     thumb.Format("%s\\%s.tbn", g_settings.GetGameSaveThumbFolder().c_str(), fileName.c_str());
2391     CLog::Log(LOGDEBUG, "Thumb  (%s)",thumb.c_str());
2392     if (!CFile::Exists(thumb))
2393     {
2394       CStdString titleimageXBX;
2395       CStdString saveimageXBX;
2396
2397       CUtil::AddFileToFolder(m_strPath, "titleimage.xbx", titleimageXBX);
2398       CUtil::AddFileToFolder(m_strPath,"saveimage.xbx",saveimageXBX);
2399
2400       /*if (CFile::Exists(saveimageXBX))
2401       {
2402         CUtil::CacheXBEIcon(saveimageXBX, thumb);
2403         CLog::Log(LOGDEBUG, "saveimageXBX  (%s)",saveimageXBX.c_str());
2404       }*/
2405       if (CFile::Exists(titleimageXBX))
2406       {
2407         CLog::Log(LOGDEBUG, "titleimageXBX  (%s)",titleimageXBX.c_str());
2408         CUtil::CacheXBEIcon(titleimageXBX, thumb);
2409       }
2410     }
2411     return thumb;
2412   }
2413   return "";
2414 }
2415
2416 void CFileItem::SetCachedProgramThumb()
2417 {
2418   // don't set any thumb for programs on DVD, as they're bound to be named the
2419   // same (D:\default.xbe).
2420   if (IsParentFolder()) return;
2421   CStdString thumb(GetCachedProgramThumb());
2422   if (CFile::Exists(thumb))
2423     SetThumbnailImage(thumb);
2424 }
2425
2426 void CFileItem::SetUserProgramThumb()
2427 {
2428   if (m_bIsShareOrDrive) return;
2429   if (IsParentFolder()) return;
2430
2431   if (IsShortCut())
2432   {
2433     CShortcut shortcut;
2434     if ( shortcut.Create( m_strPath ) )
2435     {
2436       // use the shortcut's thumb
2437       if (!shortcut.m_strThumb.IsEmpty())
2438         m_strThumbnailImage = shortcut.m_strThumb;
2439       else
2440       {
2441         CFileItem item(shortcut.m_strPath,false);
2442         item.SetUserProgramThumb();
2443         m_strThumbnailImage = item.m_strThumbnailImage;
2444       }
2445       return;
2446     }
2447   }
2448   // 1.  Try <filename>.tbn
2449   CStdString fileThumb(GetTBNFile());
2450   CStdString thumb(GetCachedProgramThumb());
2451   if (CFile::Exists(fileThumb))
2452   { // cache
2453     CPicture pic;
2454     if (pic.DoCreateThumbnail(fileThumb, thumb))
2455       SetThumbnailImage(thumb);
2456   }
2457   else if (IsXBE())
2458   {
2459     // 2. check for avalaunch_icon.jpg
2460     CStdString directory;
2461     CUtil::GetDirectory(m_strPath, directory);
2462     CStdString avalaunchIcon;
2463     CUtil::AddFileToFolder(directory, "avalaunch_icon.jpg", avalaunchIcon);
2464     if (CFile::Exists(avalaunchIcon))
2465     {
2466       CPicture pic;
2467       if (pic.DoCreateThumbnail(avalaunchIcon, thumb))
2468         SetThumbnailImage(thumb);
2469     }
2470     else if (CUtil::CacheXBEIcon(m_strPath, thumb))
2471       SetThumbnailImage(thumb);
2472   }
2473   else if (m_bIsFolder)
2474   {
2475     // 3. cache the folder image
2476     CStdString folderThumb(GetFolderThumb());
2477     if (CFile::Exists(folderThumb))
2478     {
2479       CPicture pic;
2480       if (pic.DoCreateThumbnail(folderThumb, thumb))
2481         SetThumbnailImage(thumb);
2482     }
2483   }
2484 }
2485
2486 /*void CFileItem::SetThumb()
2487 {
2488   // we need to know the type of file at this point
2489   // as differing views have differing inheritance rules for thumbs:
2490
2491   // Videos:
2492   // Folders only use <foldername>/folder.jpg or <foldername>.tbn
2493   // Files use <filename>.tbn
2494   //  * Thumbs are cached from here using file or folder path
2495
2496   // Music:
2497   // Folders only use <foldername>/folder.jpg or <foldername>.tbn
2498   // Files use <filename>.tbn or the album/path cached thumb or inherit from the folder
2499   //  * Thumbs are cached from here using file or folder path
2500
2501   // Programs:
2502   // Folders only use <foldername>/folder.jpg or <foldername>.tbn
2503   // Files use <filename>.tbn or the embedded xbe.  Shortcuts have the additional step of the <thumbnail> tag to check
2504   //  * Thumbs are cached from here using file or folder path
2505
2506   // Pictures:
2507   // Folders use <foldername>/folder.jpg or <foldername>.tbn, or auto-generated from 4 images in the folder
2508   // Files use <filename>.tbn or a resized version of the picture
2509   //  * Thumbs are cached from here using file or folder path
2510
2511 }*/
2512
2513 void CFileItemList::SetProgramThumbs()
2514 {
2515   // TODO: Is there a speed up if we cache the program thumbs first?
2516   for (unsigned int i = 0; i < m_items.size(); i++)
2517   {
2518     CFileItem *pItem = m_items[i];
2519     if (pItem->IsParentFolder())
2520       continue;
2521     pItem->SetCachedProgramThumb();
2522     if (!pItem->HasThumbnail())
2523       pItem->SetUserProgramThumb();
2524   }
2525 }
2526
2527 bool CFileItem::LoadMusicTag()
2528 {
2529   // not audio
2530   if (!IsAudio())
2531     return false;
2532   // already loaded?
2533   if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
2534     return true;
2535   // check db
2536   CMusicDatabase musicDatabase;
2537   CSong song;
2538   musicDatabase.Open();
2539   if (musicDatabase.GetSongByFileName(m_strPath, song))
2540   {
2541     GetMusicInfoTag()->SetSong(song);
2542     return true;
2543   }
2544   // load tag from file
2545   else
2546   {
2547     CLog::Log(LOGDEBUG, "%s: loading tag information for file: %s", __FUNCTION__, m_strPath.c_str());
2548     CMusicInfoTagLoaderFactory factory;
2549     CMusicInfoTag tag;
2550     auto_ptr<IMusicInfoTagLoader> pLoader (factory.CreateLoader(m_strPath));
2551     if (NULL != pLoader.get())
2552     {
2553       if (pLoader->Load(m_strPath, *GetMusicInfoTag()))
2554         return true;
2555     }
2556   }
2557   return false;
2558 }
2559
2560 void CFileItem::SetCachedGameSavesThumb()
2561 {
2562   if (IsParentFolder()) return;
2563   CStdString thumb(GetCachedGameSaveThumb());
2564   if (CFile::Exists(thumb))
2565     SetThumbnailImage(thumb);
2566 }
2567
2568 void CFileItemList::SetCachedGameSavesThumbs()
2569 {
2570   // TODO: Investigate caching time to see if it speeds things up
2571   for (unsigned int i = 0; i < m_items.size(); ++i)
2572   {
2573     CFileItem* pItem = m_items[i];
2574     pItem->SetCachedGameSavesThumb();
2575   }
2576 }
2577
2578 void CFileItemList::SetGameSavesThumbs()
2579 {
2580   // No User thumbs
2581   // TODO: Is there a speed up if we cache the program thumbs first?
2582   for (unsigned int i = 0; i < m_items.size(); i++)
2583   {
2584     CFileItem *pItem = m_items[i];
2585     if (pItem->IsParentFolder())
2586       continue;
2587     pItem->SetCachedGameSavesThumb();  // was  pItem->SetCachedProgramThumb(); oringally
2588   }
2589 }
2590
2591 void CFileItemList::Swap(unsigned int item1, unsigned int item2)
2592 {
2593   if (item1 != item2 && item1 < m_items.size() && item2 < m_items.size())
2594     swap(m_items[item1], m_items[item2]);
2595 }
2596
2597 void CFileItemList::UpdateItem(const CFileItem *item)
2598 {
2599   if (!item) return;
2600   CFileItem *oldItem = Get(item->m_strPath);
2601   if (oldItem)
2602     *oldItem = *item;
2603 }
2604
2605 void CFileItemList::AddSortMethod(SORT_METHOD sortMethod, int buttonLabel, const LABEL_MASKS &labelMasks)
2606 {
2607   SORT_METHOD_DETAILS sort;
2608   sort.m_sortMethod=sortMethod;
2609   sort.m_buttonLabel=buttonLabel;
2610   sort.m_labelMasks=labelMasks;
2611
2612   m_sortDetails.push_back(sort);
2613 }
2614
2615 void CFileItemList::SetReplaceListing(bool replace)
2616 {
2617   m_replaceListing = replace;
2618 }
2619
2620 void CFileItemList::ClearSortState()
2621 {
2622   m_sortMethod=SORT_METHOD_NONE;
2623   m_sortOrder=SORT_ORDER_NONE;
2624 }
2625