summaryrefslogtreecommitdiffstats
path: root/xbmc/guilib/TextureBundleXBT.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/guilib/TextureBundleXBT.cpp')
-rw-r--r--xbmc/guilib/TextureBundleXBT.cpp301
1 files changed, 301 insertions, 0 deletions
diff --git a/xbmc/guilib/TextureBundleXBT.cpp b/xbmc/guilib/TextureBundleXBT.cpp
new file mode 100644
index 0000000..981ddad
--- /dev/null
+++ b/xbmc/guilib/TextureBundleXBT.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "TextureBundleXBT.h"
+
+#include "ServiceBroker.h"
+#include "Texture.h"
+#include "URL.h"
+#include "XBTF.h"
+#include "XBTFReader.h"
+#include "commons/ilog.h"
+#include "filesystem/SpecialProtocol.h"
+#include "filesystem/XbtManager.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WinSystem.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <exception>
+
+#include <lzo/lzo1x.h>
+#include <lzo/lzoconf.h>
+
+
+#ifdef TARGET_WINDOWS_DESKTOP
+#ifdef NDEBUG
+#pragma comment(lib,"lzo2.lib")
+#else
+#pragma comment(lib, "lzo2d.lib")
+#endif
+#endif
+
+CTextureBundleXBT::CTextureBundleXBT()
+ : m_TimeStamp{0}
+ , m_themeBundle{false}
+{
+}
+
+CTextureBundleXBT::CTextureBundleXBT(bool themeBundle)
+ : m_TimeStamp{0}
+ , m_themeBundle{themeBundle}
+{
+}
+
+CTextureBundleXBT::~CTextureBundleXBT(void)
+{
+ CloseBundle();
+}
+
+void CTextureBundleXBT::CloseBundle()
+{
+ if (m_XBTFReader != nullptr && m_XBTFReader->IsOpen())
+ {
+ XFILE::CXbtManager::GetInstance().Release(CURL(m_path));
+ CLog::Log(LOGDEBUG, "{} - Closed {}bundle", __FUNCTION__, m_themeBundle ? "theme " : "");
+ }
+}
+
+bool CTextureBundleXBT::OpenBundle()
+{
+ // Find the correct texture file (skin or theme)
+ if (m_themeBundle)
+ {
+ // if we are the theme bundle, we only load if the user has chosen
+ // a valid theme (or the skin has a default one)
+ std::string theme = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOOKANDFEEL_SKINTHEME);
+ if (!theme.empty() && !StringUtils::EqualsNoCase(theme, "SKINDEFAULT"))
+ {
+ std::string themeXBT(URIUtils::ReplaceExtension(theme, ".xbt"));
+ m_path = URIUtils::AddFileToFolder(CServiceBroker::GetWinSystem()->GetGfxContext().GetMediaDir(), "media", themeXBT);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ m_path = URIUtils::AddFileToFolder(CServiceBroker::GetWinSystem()->GetGfxContext().GetMediaDir(), "media", "Textures.xbt");
+ }
+
+ m_path = CSpecialProtocol::TranslatePathConvertCase(m_path);
+
+ // Load the texture file
+ if (!XFILE::CXbtManager::GetInstance().GetReader(CURL(m_path), m_XBTFReader))
+ {
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "{} - Opened bundle {}", __FUNCTION__, m_path);
+
+ m_TimeStamp = m_XBTFReader->GetLastModificationTimestamp();
+
+ if (lzo_init() != LZO_E_OK)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool CTextureBundleXBT::HasFile(const std::string& Filename)
+{
+ if ((m_XBTFReader == nullptr || !m_XBTFReader->IsOpen()) && !OpenBundle())
+ return false;
+
+ if (m_XBTFReader->GetLastModificationTimestamp() > m_TimeStamp)
+ {
+ CLog::Log(LOGINFO, "Texture bundle has changed, reloading");
+ if (!OpenBundle())
+ return false;
+ }
+
+ std::string name = Normalize(Filename);
+ return m_XBTFReader->Exists(name);
+}
+
+std::vector<std::string> CTextureBundleXBT::GetTexturesFromPath(const std::string& path)
+{
+ if (path.size() > 1 && path[1] == ':')
+ return {};
+
+ if ((m_XBTFReader == nullptr || !m_XBTFReader->IsOpen()) && !OpenBundle())
+ return {};
+
+ std::string testPath = Normalize(path);
+ URIUtils::AddSlashAtEnd(testPath);
+
+ std::vector<std::string> textures;
+ std::vector<CXBTFFile> files = m_XBTFReader->GetFiles();
+ for (size_t i = 0; i < files.size(); i++)
+ {
+ std::string filePath = files[i].GetPath();
+ if (StringUtils::StartsWithNoCase(filePath, testPath))
+ textures.emplace_back(std::move(filePath));
+ }
+
+ return textures;
+}
+
+bool CTextureBundleXBT::LoadTexture(const std::string& filename,
+ std::unique_ptr<CTexture>& texture,
+ int& width,
+ int& height)
+{
+ std::string name = Normalize(filename);
+
+ CXBTFFile file;
+ if (!m_XBTFReader->Get(name, file))
+ return false;
+
+ if (file.GetFrames().empty())
+ return false;
+
+ const CXBTFFrame& frame = file.GetFrames().at(0);
+ if (!ConvertFrameToTexture(filename, frame, texture))
+ {
+ return false;
+ }
+
+ width = frame.GetWidth();
+ height = frame.GetHeight();
+
+ return true;
+}
+
+bool CTextureBundleXBT::LoadAnim(const std::string& filename,
+ std::vector<std::pair<std::unique_ptr<CTexture>, int>>& textures,
+ int& width,
+ int& height,
+ int& nLoops)
+{
+ std::string name = Normalize(filename);
+
+ CXBTFFile file;
+ if (!m_XBTFReader->Get(name, file))
+ return false;
+
+ if (file.GetFrames().empty())
+ return false;
+
+ size_t nTextures = file.GetFrames().size();
+ textures.reserve(nTextures);
+
+ for (size_t i = 0; i < nTextures; i++)
+ {
+ CXBTFFrame& frame = file.GetFrames().at(i);
+
+ std::unique_ptr<CTexture> texture;
+ if (!ConvertFrameToTexture(filename, frame, texture))
+ return false;
+
+ textures.emplace_back(std::move(texture), frame.GetDuration());
+ }
+
+ width = file.GetFrames().at(0).GetWidth();
+ height = file.GetFrames().at(0).GetHeight();
+ nLoops = file.GetLoop();
+
+ return true;
+}
+
+bool CTextureBundleXBT::ConvertFrameToTexture(const std::string& name,
+ const CXBTFFrame& frame,
+ std::unique_ptr<CTexture>& texture)
+{
+ // found texture - allocate the necessary buffers
+ std::vector<unsigned char> buffer(static_cast<size_t>(frame.GetPackedSize()));
+
+ // load the compressed texture
+ if (!m_XBTFReader->Load(frame, buffer.data()))
+ {
+ CLog::Log(LOGERROR, "Error loading texture: {}", name);
+ return false;
+ }
+
+ // check if it's packed with lzo
+ if (frame.IsPacked())
+ { // unpack
+ std::vector<unsigned char> unpacked(static_cast<size_t>(frame.GetUnpackedSize()));
+ lzo_uint s = (lzo_uint)frame.GetUnpackedSize();
+ if (lzo1x_decompress_safe(buffer.data(), static_cast<lzo_uint>(buffer.size()), unpacked.data(),
+ &s, NULL) != LZO_E_OK ||
+ s != frame.GetUnpackedSize())
+ {
+ CLog::Log(LOGERROR, "Error loading texture: {}: Decompression error", name);
+ return false;
+ }
+ buffer = std::move(unpacked);
+ }
+
+ // create an xbmc texture
+ texture = CTexture::CreateTexture();
+ texture->LoadFromMemory(frame.GetWidth(), frame.GetHeight(), 0, frame.GetFormat(),
+ frame.HasAlpha(), buffer.data());
+
+ return true;
+}
+
+void CTextureBundleXBT::SetThemeBundle(bool themeBundle)
+{
+ m_themeBundle = themeBundle;
+}
+
+// normalize to how it's stored within the bundle
+// lower case + using forward slash rather than back slash
+std::string CTextureBundleXBT::Normalize(std::string name)
+{
+ StringUtils::Trim(name);
+ StringUtils::ToLower(name);
+ StringUtils::Replace(name, '\\', '/');
+
+ return name;
+}
+
+std::vector<uint8_t> CTextureBundleXBT::UnpackFrame(const CXBTFReader& reader,
+ const CXBTFFrame& frame)
+{
+ // load the compressed texture
+ std::vector<uint8_t> packedBuffer(static_cast<size_t>(frame.GetPackedSize()));
+ if (!reader.Load(frame, packedBuffer.data()))
+ {
+ CLog::Log(LOGERROR, "CTextureBundleXBT: error loading frame");
+ return {};
+ }
+
+ // if the frame isn't packed there's nothing else to be done
+ if (!frame.IsPacked())
+ return packedBuffer;
+
+ // make sure lzo is initialized
+ if (lzo_init() != LZO_E_OK)
+ {
+ CLog::Log(LOGERROR, "CTextureBundleXBT: failed to initialize lzo");
+ return {};
+ }
+
+ lzo_uint size = static_cast<lzo_uint>(frame.GetUnpackedSize());
+ std::vector<uint8_t> unpackedBuffer(static_cast<size_t>(frame.GetUnpackedSize()));
+ if (lzo1x_decompress_safe(packedBuffer.data(), static_cast<lzo_uint>(packedBuffer.size()),
+ unpackedBuffer.data(), &size, nullptr) != LZO_E_OK ||
+ size != frame.GetUnpackedSize())
+ {
+ CLog::Log(LOGERROR,
+ "CTextureBundleXBT: failed to decompress frame with {} unpacked bytes to {} bytes",
+ frame.GetPackedSize(), frame.GetUnpackedSize());
+ return {};
+ }
+
+ return unpackedBuffer;
+}