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.
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"
31 m_xbtfReader(nullptr),
33 m_frameStartPositions(),
42 bool CXbtFile::Open(const CURL
& url
)
48 xbtUrl
.SetOptions("");
50 if (!GetReaderAndFile(url
, m_xbtfReader
, m_xbtfFile
))
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();
66 m_positionWithinFrame
= 0;
69 m_unpackedFrames
.resize(frames
.size());
74 void CXbtFile::Close()
76 m_unpackedFrames
.clear();
78 m_positionWithinFrame
= 0;
80 m_frameStartPositions
.clear();
84 bool CXbtFile::Exists(const CURL
& url
)
87 return GetFile(url
, dummy
);
90 int64_t CXbtFile::GetPosition()
95 return m_positionTotal
;
98 int64_t CXbtFile::GetLength()
103 return static_cast<int>(m_xbtfFile
.GetUnpackedSize());
106 int CXbtFile::Stat(struct __stat64
*buffer
)
111 return Stat(m_url
, buffer
);
114 int CXbtFile::Stat(const CURL
& url
, struct __stat64
* buffer
)
121 // check if the file exists
122 CXBTFReaderPtr reader
;
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()))
130 // stat the XBT file itself
131 if (XFILE::CFile::Stat(url
.GetHostName(), buffer
) != 0)
134 buffer
->st_mode
= _S_IFDIR
;
138 // stat the XBT file itself
139 if (XFILE::CFile::Stat(url
.GetHostName(), buffer
) != 0)
142 buffer
->st_size
= file
.GetUnpackedSize();
147 ssize_t
CXbtFile::Read(void* lpBuf
, size_t uiBufSize
)
149 if (lpBuf
== nullptr || !m_open
)
153 if (m_xbtfFile
.GetFrames().empty() || m_positionTotal
>= GetLength())
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())
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
));
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;
209 int64_t CXbtFile::Seek(int64_t iFilePosition
, int iWhence
)
214 int64_t newPosition
= m_positionTotal
;
218 newPosition
= iFilePosition
;
222 newPosition
+= iFilePosition
;
226 newPosition
= GetLength() + iFilePosition
;
229 // unsupported seek mode
234 // can't seek before the beginning or after the end of the file
235 if (newPosition
< 0 || newPosition
>= GetLength())
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())
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
;
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;
287 m_positionTotal
= newPosition
;
288 return m_positionTotal
;
291 uint32_t CXbtFile::GetImageWidth() const
294 if (!GetFirstFrame(frame
))
297 return frame
.GetWidth();
300 uint32_t CXbtFile::GetImageHeight() const
303 if (!GetFirstFrame(frame
))
306 return frame
.GetHeight();
309 XB_FMT
CXbtFile::GetImageFormat() const
312 if (!GetFirstFrame(frame
))
313 return XB_FMT_UNKNOWN
;
315 return frame
.GetFormat();
318 KD_TEX_FMT
CXbtFile::GetKDFormat() const
321 if (!GetFirstFrame(frame
))
322 return KD_TEX_FMT_UNKNOWN
;
324 return frame
.GetKDFormat();
327 KD_TEX_FMT
CXbtFile::GetKDFormatType() const
330 if (!GetFirstFrame(frame
))
331 return KD_TEX_FMT_UNKNOWN
;
333 return frame
.GetKDFormatType();
336 KD_TEX_ALPHA
CXbtFile::GetKDAlpha() const
339 if (!GetFirstFrame(frame
))
340 return KD_TEX_ALPHA_OPAQUE
;
342 return frame
.GetKDAlpha();
345 KD_TEX_SWIZ
CXbtFile::GetKDSwizzle() const
348 if (!GetFirstFrame(frame
))
349 return KD_TEX_SWIZ_RGBA
;
351 return frame
.GetKDSwizzle();
354 bool CXbtFile::HasImageAlpha() const
357 if (!GetFirstFrame(frame
))
360 return frame
.HasAlpha();
363 bool CXbtFile::GetFirstFrame(CXBTFFrame
& frame
) const
368 const auto& frames
= m_xbtfFile
.GetFrames();
372 frame
= frames
.at(0);
376 bool CXbtFile::GetReader(const CURL
& url
, CXBTFReaderPtr
& reader
)
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
))
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
);