ActiveAE: fix incorrect return values of GetSpace
[vuplus_xbmc] / xbmc / TextureCache.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
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 XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "TextureCache.h"
22 #include "TextureCacheJob.h"
23 #include "filesystem/File.h"
24 #include "profiles/ProfilesManager.h"
25 #include "threads/SingleLock.h"
26 #include "utils/Crc32.h"
27 #include "settings/AdvancedSettings.h"
28 #include "utils/log.h"
29 #include "utils/URIUtils.h"
30 #include "utils/StringUtils.h"
31 #include "URL.h"
32 #include "utils/StringUtils.h"
33
34 using namespace XFILE;
35
36 CTextureCache &CTextureCache::Get()
37 {
38   static CTextureCache s_cache;
39   return s_cache;
40 }
41
42 CTextureCache::CTextureCache() : CJobQueue(false, 1, CJob::PRIORITY_LOW_PAUSABLE)
43 {
44 }
45
46 CTextureCache::~CTextureCache()
47 {
48 }
49
50 void CTextureCache::Initialize()
51 {
52   CSingleLock lock(m_databaseSection);
53   if (!m_database.IsOpen())
54     m_database.Open();
55 }
56
57 void CTextureCache::Deinitialize()
58 {
59   CancelJobs();
60   CSingleLock lock(m_databaseSection);
61   m_database.Close();
62 }
63
64 bool CTextureCache::IsCachedImage(const CStdString &url) const
65 {
66   if (url != "-" && !CURL::IsFullPath(url))
67     return true;
68   if (URIUtils::IsInPath(url, "special://skin/") ||
69       URIUtils::IsInPath(url, "androidapp://")   ||
70       URIUtils::IsInPath(url, CProfilesManager::Get().GetThumbnailsFolder()))
71     return true;
72   return false;
73 }
74
75 bool CTextureCache::HasCachedImage(const CStdString &url)
76 {
77   CTextureDetails details;
78   CStdString cachedImage(GetCachedImage(url, details));
79   return (!cachedImage.empty() && cachedImage != url);
80 }
81
82 CStdString CTextureCache::GetCachedImage(const CStdString &image, CTextureDetails &details, bool trackUsage)
83 {
84   CStdString url = CTextureUtils::UnwrapImageURL(image);
85
86   if (IsCachedImage(url))
87     return url;
88
89   // lookup the item in the database
90   if (GetCachedTexture(url, details))
91   {
92     if (trackUsage)
93       IncrementUseCount(details);
94     return GetCachedPath(details.file);
95   }
96   return "";
97 }
98
99 bool CTextureCache::CanCacheImageURL(const CURL &url)
100 {
101   return (url.GetUserName().empty() || url.GetUserName() == "music");
102 }
103
104 CStdString CTextureCache::CheckCachedImage(const CStdString &url, bool returnDDS, bool &needsRecaching)
105 {
106   CTextureDetails details;
107   CStdString path(GetCachedImage(url, details, true));
108   needsRecaching = !details.hash.empty();
109   if (!path.empty())
110   {
111     if (!needsRecaching && returnDDS && !URIUtils::IsInPath(url, "special://skin/")) // TODO: should skin images be .dds'd (currently they're not necessarily writeable)
112     { // check for dds version
113       CStdString ddsPath = URIUtils::ReplaceExtension(path, ".dds");
114       if (CFile::Exists(ddsPath))
115         return ddsPath;
116       if (g_advancedSettings.m_useDDSFanart)
117         AddJob(new CTextureDDSJob(path));
118     }
119     return path;
120   }
121   return "";
122 }
123
124 void CTextureCache::BackgroundCacheImage(const CStdString &url)
125 {
126   CTextureDetails details;
127   CStdString path(GetCachedImage(url, details));
128   if (!path.empty() && details.hash.empty())
129     return; // image is already cached and doesn't need to be checked further
130
131   // needs (re)caching
132   AddJob(new CTextureCacheJob(CTextureUtils::UnwrapImageURL(url), details.hash));
133 }
134
135 bool CTextureCache::CacheImage(const CStdString &image, CTextureDetails &details)
136 {
137   CStdString path = GetCachedImage(image, details);
138   if (path.empty()) // not cached
139     path = CacheImage(image, NULL, &details);
140
141   return !path.empty();
142 }
143
144 CStdString CTextureCache::CacheImage(const CStdString &image, CBaseTexture **texture, CTextureDetails *details)
145 {
146   CStdString url = CTextureUtils::UnwrapImageURL(image);
147   CSingleLock lock(m_processingSection);
148   if (m_processing.find(url) == m_processing.end())
149   {
150     m_processing.insert(url);
151     lock.Leave();
152     // cache the texture directly
153     CTextureCacheJob job(url);
154     bool success = job.CacheTexture(texture);
155     OnCachingComplete(success, &job);
156     if (success && details)
157       *details = job.m_details;
158     return success ? GetCachedPath(job.m_details.file) : "";
159   }
160   lock.Leave();
161
162   // wait for currently processing job to end.
163   while (true)
164   {
165     m_completeEvent.WaitMSec(1000);
166     {
167       CSingleLock lock(m_processingSection);
168       if (m_processing.find(url) == m_processing.end())
169         break;
170     }
171   }
172   CTextureDetails tempDetails;
173   if (!details)
174     details = &tempDetails;
175   return GetCachedImage(url, *details, true);
176 }
177
178 void CTextureCache::ClearCachedImage(const CStdString &url, bool deleteSource /*= false */)
179 {
180   // TODO: This can be removed when the texture cache covers everything.
181   CStdString path = deleteSource ? url : "";
182   CStdString cachedFile;
183   if (ClearCachedTexture(url, cachedFile))
184     path = GetCachedPath(cachedFile);
185   if (CFile::Exists(path))
186     CFile::Delete(path);
187   path = URIUtils::ReplaceExtension(path, ".dds");
188   if (CFile::Exists(path))
189     CFile::Delete(path);
190 }
191
192 bool CTextureCache::ClearCachedImage(int id)
193 {
194   CStdString cachedFile;
195   if (ClearCachedTexture(id, cachedFile))
196   {
197     cachedFile = GetCachedPath(cachedFile);
198     if (CFile::Exists(cachedFile))
199       CFile::Delete(cachedFile);
200     cachedFile = URIUtils::ReplaceExtension(cachedFile, ".dds");
201     if (CFile::Exists(cachedFile))
202       CFile::Delete(cachedFile);
203     return true;
204   }
205   return false;
206 }
207
208 bool CTextureCache::GetCachedTexture(const CStdString &url, CTextureDetails &details)
209 {
210   CSingleLock lock(m_databaseSection);
211   return m_database.GetCachedTexture(url, details);
212 }
213
214 bool CTextureCache::AddCachedTexture(const CStdString &url, const CTextureDetails &details)
215 {
216   CSingleLock lock(m_databaseSection);
217   return m_database.AddCachedTexture(url, details);
218 }
219
220 void CTextureCache::IncrementUseCount(const CTextureDetails &details)
221 {
222   static const size_t count_before_update = 100;
223   CSingleLock lock(m_useCountSection);
224   m_useCounts.reserve(count_before_update);
225   m_useCounts.push_back(details);
226   if (m_useCounts.size() >= count_before_update)
227   {
228     AddJob(new CTextureUseCountJob(m_useCounts));
229     m_useCounts.clear();
230   }
231 }
232
233 bool CTextureCache::SetCachedTextureValid(const CStdString &url, bool updateable)
234 {
235   CSingleLock lock(m_databaseSection);
236   return m_database.SetCachedTextureValid(url, updateable);
237 }
238
239 bool CTextureCache::ClearCachedTexture(const CStdString &url, CStdString &cachedURL)
240 {
241   CSingleLock lock(m_databaseSection);
242   return m_database.ClearCachedTexture(url, cachedURL);
243 }
244
245 bool CTextureCache::ClearCachedTexture(int id, CStdString &cachedURL)
246 {
247   CSingleLock lock(m_databaseSection);
248   return m_database.ClearCachedTexture(id, cachedURL);
249 }
250
251 CStdString CTextureCache::GetCacheFile(const CStdString &url)
252 {
253   Crc32 crc;
254   crc.ComputeFromLowerCase(url);
255   CStdString hex = StringUtils::Format("%08x", (unsigned int)crc);
256   CStdString hash = StringUtils::Format("%c/%s", hex[0], hex.c_str());
257   return hash;
258 }
259
260 CStdString CTextureCache::GetCachedPath(const CStdString &file)
261 {
262   return URIUtils::AddFileToFolder(CProfilesManager::Get().GetThumbnailsFolder(), file);
263 }
264
265 void CTextureCache::OnCachingComplete(bool success, CTextureCacheJob *job)
266 {
267   if (success)
268   {
269     if (job->m_oldHash == job->m_details.hash)
270       SetCachedTextureValid(job->m_url, job->m_details.updateable);
271     else
272       AddCachedTexture(job->m_url, job->m_details);
273   }
274
275   { // remove from our processing list
276     CSingleLock lock(m_processingSection);
277     std::set<CStdString>::iterator i = m_processing.find(job->m_url);
278     if (i != m_processing.end())
279       m_processing.erase(i);
280   }
281
282   m_completeEvent.Set();
283
284   // TODO: call back to the UI indicating that it can update it's image...
285   if (success && g_advancedSettings.m_useDDSFanart && !job->m_details.file.empty())
286     AddJob(new CTextureDDSJob(GetCachedPath(job->m_details.file)));
287 }
288
289 void CTextureCache::OnJobComplete(unsigned int jobID, bool success, CJob *job)
290 {
291   if (strcmp(job->GetType(), kJobTypeCacheImage) == 0)
292     OnCachingComplete(success, (CTextureCacheJob *)job);
293   return CJobQueue::OnJobComplete(jobID, success, job);
294 }
295
296 void CTextureCache::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job)
297 {
298   if (strcmp(job->GetType(), kJobTypeCacheImage) == 0 && !progress)
299   { // check our processing list
300     {
301       CSingleLock lock(m_processingSection);
302       const CTextureCacheJob *cacheJob = (CTextureCacheJob *)job;
303       std::set<CStdString>::iterator i = m_processing.find(cacheJob->m_url);
304       if (i == m_processing.end())
305       {
306         m_processing.insert(cacheJob->m_url);
307         return;
308       }
309     }
310     CancelJob(job);
311   }
312   else
313     CJobQueue::OnJobProgress(jobID, progress, total, job);
314 }
315
316 bool CTextureCache::Export(const CStdString &image, const CStdString &destination, bool overwrite)
317 {
318   CTextureDetails details;
319   CStdString cachedImage(GetCachedImage(image, details));
320   if (!cachedImage.empty())
321   {
322     CStdString dest = destination + URIUtils::GetExtension(cachedImage);
323     if (overwrite || !CFile::Exists(dest))
324     {
325       if (CFile::Cache(cachedImage, dest))
326         return true;
327       CLog::Log(LOGERROR, "%s failed exporting '%s' to '%s'", __FUNCTION__, cachedImage.c_str(), dest.c_str());
328     }
329   }
330   return false;
331 }
332
333 bool CTextureCache::Export(const CStdString &image, const CStdString &destination)
334 {
335   CTextureDetails details;
336   CStdString cachedImage(GetCachedImage(image, details));
337   if (!cachedImage.empty())
338   {
339     if (CFile::Cache(cachedImage, destination))
340       return true;
341     CLog::Log(LOGERROR, "%s failed exporting '%s' to '%s'", __FUNCTION__, cachedImage.c_str(), destination.c_str());
342   }
343   return false;
344 }