Merge pull request #26373 from ksooo/app-fix-multi-resolve-playback
[xbmc.git] / xbmc / filesystem / XbtFile.cpp
blob34860dac4358eb1f2376aeebbb8a235ef320e75d
1 /*
2 * Copyright (C) 2015-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "XbtFile.h"
11 #include "URL.h"
12 #include "filesystem/File.h"
13 #include "filesystem/XbtManager.h"
14 #include "guilib/TextureBundleXBT.h"
15 #include "guilib/TextureFormats.h"
16 #include "guilib/XBTFReader.h"
17 #include "utils/StringUtils.h"
19 #include <algorithm>
20 #include <climits>
21 #include <cstring>
22 #include <memory>
23 #include <string>
24 #include <utility>
26 namespace XFILE
29 CXbtFile::CXbtFile()
30 : m_url(),
31 m_xbtfReader(nullptr),
32 m_xbtfFile(),
33 m_frameStartPositions(),
34 m_unpackedFrames()
35 { }
37 CXbtFile::~CXbtFile()
39 Close();
42 bool CXbtFile::Open(const CURL& url)
44 if (m_open)
45 return false;
47 CURL xbtUrl(url);
48 xbtUrl.SetOptions("");
50 if (!GetReaderAndFile(url, m_xbtfReader, m_xbtfFile))
51 return false;
53 m_url = url;
54 m_open = true;
56 uint64_t frameStartPosition = 0;
57 const auto& frames = m_xbtfFile.GetFrames();
58 for (const auto& frame : frames)
60 m_frameStartPositions.push_back(frameStartPosition);
62 frameStartPosition += frame.GetUnpackedSize();
65 m_frameIndex = 0;
66 m_positionWithinFrame = 0;
67 m_positionTotal = 0;
69 m_unpackedFrames.resize(frames.size());
71 return true;
74 void CXbtFile::Close()
76 m_unpackedFrames.clear();
77 m_frameIndex = 0;
78 m_positionWithinFrame = 0;
79 m_positionTotal = 0;
80 m_frameStartPositions.clear();
81 m_open = false;
84 bool CXbtFile::Exists(const CURL& url)
86 CXBTFFile dummy;
87 return GetFile(url, dummy);
90 int64_t CXbtFile::GetPosition()
92 if (!m_open)
93 return -1;
95 return m_positionTotal;
98 int64_t CXbtFile::GetLength()
100 if (!m_open)
101 return -1;
103 return static_cast<int>(m_xbtfFile.GetUnpackedSize());
106 int CXbtFile::Stat(struct __stat64 *buffer)
108 if (!m_open)
109 return -1;
111 return Stat(m_url, buffer);
114 int CXbtFile::Stat(const CURL& url, struct __stat64* buffer)
116 if (!buffer)
117 return -1;
119 *buffer = {};
121 // check if the file exists
122 CXBTFReaderPtr reader;
123 CXBTFFile file;
124 if (!GetReaderAndFile(url, reader, file))
126 // check if the URL points to the XBT file itself
127 if (!url.GetFileName().empty() || !CFile::Exists(url.GetHostName()))
128 return -1;
130 // stat the XBT file itself
131 if (XFILE::CFile::Stat(url.GetHostName(), buffer) != 0)
132 return -1;
134 buffer->st_mode = _S_IFDIR;
135 return 0;
138 // stat the XBT file itself
139 if (XFILE::CFile::Stat(url.GetHostName(), buffer) != 0)
140 return -1;
142 buffer->st_size = file.GetUnpackedSize();
144 return 0;
147 ssize_t CXbtFile::Read(void* lpBuf, size_t uiBufSize)
149 if (lpBuf == nullptr || !m_open)
150 return -1;
152 // nothing to read
153 if (m_xbtfFile.GetFrames().empty() || m_positionTotal >= GetLength())
154 return 0;
156 // we can't read more than is left
157 if (static_cast<int64_t>(uiBufSize) > GetLength() - m_positionTotal)
158 uiBufSize = static_cast<ssize_t>(GetLength() - m_positionTotal);
160 // we can't read more than we can signal with the return value
161 if (uiBufSize > SSIZE_MAX)
162 uiBufSize = SSIZE_MAX;
164 const auto& frames = m_xbtfFile.GetFrames();
166 size_t remaining = uiBufSize;
167 while (remaining > 0)
169 const CXBTFFrame& frame = frames[m_frameIndex];
171 // check if we have already unpacked the current frame
172 if (m_unpackedFrames[m_frameIndex].empty())
174 // unpack the data from the current frame
175 std::vector<uint8_t> unpackedFrame =
176 CTextureBundleXBT::UnpackFrame(*m_xbtfReader.get(), frame);
177 if (unpackedFrame.empty())
179 Close();
180 return -1;
183 m_unpackedFrames[m_frameIndex] = std::move(unpackedFrame);
186 // determine how many bytes we need to copy from the current frame
187 uint64_t remainingBytesInFrame = frame.GetUnpackedSize() - m_positionWithinFrame;
188 size_t bytesToCopy = remaining;
189 if (remainingBytesInFrame <= SIZE_MAX)
190 bytesToCopy = std::min(remaining, static_cast<size_t>(remainingBytesInFrame));
192 // copy the data
193 memcpy(lpBuf, m_unpackedFrames[m_frameIndex].data() + m_positionWithinFrame, bytesToCopy);
194 m_positionWithinFrame += bytesToCopy;
195 m_positionTotal += bytesToCopy;
196 remaining -= bytesToCopy;
198 // check if we need to go to the next frame and there is a next frame
199 if (m_positionWithinFrame >= frame.GetUnpackedSize() && m_frameIndex < frames.size() - 1)
201 m_positionWithinFrame = 0;
202 m_frameIndex += 1;
206 return uiBufSize;
209 int64_t CXbtFile::Seek(int64_t iFilePosition, int iWhence)
211 if (!m_open)
212 return -1;
214 int64_t newPosition = m_positionTotal;
215 switch (iWhence)
217 case SEEK_SET:
218 newPosition = iFilePosition;
219 break;
221 case SEEK_CUR:
222 newPosition += iFilePosition;
223 break;
225 case SEEK_END:
226 newPosition = GetLength() + iFilePosition;
227 break;
229 // unsupported seek mode
230 default:
231 return -1;
234 // can't seek before the beginning or after the end of the file
235 if (newPosition < 0 || newPosition >= GetLength())
236 return -1;
238 // seeking backwards doesn't require additional work
239 if (newPosition <= m_positionTotal)
241 m_positionTotal = newPosition;
242 return m_positionTotal;
245 // when seeking forward we need to unpack all frames we seek past
246 const auto& frames = m_xbtfFile.GetFrames();
247 while (m_positionTotal < newPosition)
249 const CXBTFFrame& frame = frames[m_frameIndex];
251 // check if we have already unpacked the current frame
252 if (m_unpackedFrames[m_frameIndex].empty())
254 // unpack the data from the current frame
255 std::vector<uint8_t> unpackedFrame =
256 CTextureBundleXBT::UnpackFrame(*m_xbtfReader.get(), frame);
257 if (unpackedFrame.empty())
259 Close();
260 return -1;
263 m_unpackedFrames[m_frameIndex] = std::move(unpackedFrame);
266 int64_t remainingBytesToSeek = newPosition - m_positionTotal;
267 // check if the new position is within the current frame
268 uint64_t remainingBytesInFrame = frame.GetUnpackedSize() - m_positionWithinFrame;
269 if (static_cast<uint64_t>(remainingBytesToSeek) < remainingBytesInFrame)
271 m_positionWithinFrame += remainingBytesToSeek;
272 break;
275 // otherwise move to the end of the frame
276 m_positionTotal += remainingBytesInFrame;
277 m_positionWithinFrame += remainingBytesInFrame;
279 // and go to the next frame if there is a next frame
280 if (m_frameIndex < frames.size() - 1)
282 m_positionWithinFrame = 0;
283 m_frameIndex += 1;
287 m_positionTotal = newPosition;
288 return m_positionTotal;
291 uint32_t CXbtFile::GetImageWidth() const
293 CXBTFFrame frame;
294 if (!GetFirstFrame(frame))
295 return false;
297 return frame.GetWidth();
300 uint32_t CXbtFile::GetImageHeight() const
302 CXBTFFrame frame;
303 if (!GetFirstFrame(frame))
304 return false;
306 return frame.GetHeight();
309 XB_FMT CXbtFile::GetImageFormat() const
311 CXBTFFrame frame;
312 if (!GetFirstFrame(frame))
313 return XB_FMT_UNKNOWN;
315 return frame.GetFormat();
318 KD_TEX_FMT CXbtFile::GetKDFormat() const
320 CXBTFFrame frame;
321 if (!GetFirstFrame(frame))
322 return KD_TEX_FMT_UNKNOWN;
324 return frame.GetKDFormat();
327 KD_TEX_FMT CXbtFile::GetKDFormatType() const
329 CXBTFFrame frame;
330 if (!GetFirstFrame(frame))
331 return KD_TEX_FMT_UNKNOWN;
333 return frame.GetKDFormatType();
336 KD_TEX_ALPHA CXbtFile::GetKDAlpha() const
338 CXBTFFrame frame;
339 if (!GetFirstFrame(frame))
340 return KD_TEX_ALPHA_OPAQUE;
342 return frame.GetKDAlpha();
345 KD_TEX_SWIZ CXbtFile::GetKDSwizzle() const
347 CXBTFFrame frame;
348 if (!GetFirstFrame(frame))
349 return KD_TEX_SWIZ_RGBA;
351 return frame.GetKDSwizzle();
354 bool CXbtFile::HasImageAlpha() const
356 CXBTFFrame frame;
357 if (!GetFirstFrame(frame))
358 return false;
360 return frame.HasAlpha();
363 bool CXbtFile::GetFirstFrame(CXBTFFrame& frame) const
365 if (!m_open)
366 return false;
368 const auto& frames = m_xbtfFile.GetFrames();
369 if (frames.empty())
370 return false;
372 frame = frames.at(0);
373 return true;
376 bool CXbtFile::GetReader(const CURL& url, CXBTFReaderPtr& reader)
378 CURL xbtUrl(url);
379 xbtUrl.SetOptions("");
381 return CXbtManager::GetInstance().GetReader(xbtUrl, reader);
384 bool CXbtFile::GetReaderAndFile(const CURL& url, CXBTFReaderPtr& reader, CXBTFFile& file)
386 if (!GetReader(url, reader))
387 return false;
389 CURL xbtUrl(url);
390 xbtUrl.SetOptions("");
392 // CXBTFReader stores all filenames in lower case
393 std::string fileName = xbtUrl.GetFileName();
394 StringUtils::ToLower(fileName);
396 return reader->Get(fileName, file);
399 bool CXbtFile::GetFile(const CURL& url, CXBTFFile& file)
401 CXBTFReaderPtr reader;
402 return GetReaderAndFile(url, reader, file);