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/XBTFReader.h"
16 #include "utils/StringUtils.h"
30 m_xbtfReader(nullptr),
32 m_frameStartPositions(),
41 bool CXbtFile::Open(const CURL
& url
)
47 xbtUrl
.SetOptions("");
49 if (!GetReaderAndFile(url
, m_xbtfReader
, m_xbtfFile
))
55 uint64_t frameStartPosition
= 0;
56 const auto& frames
= m_xbtfFile
.GetFrames();
57 for (const auto& frame
: frames
)
59 m_frameStartPositions
.push_back(frameStartPosition
);
61 frameStartPosition
+= frame
.GetUnpackedSize();
65 m_positionWithinFrame
= 0;
68 m_unpackedFrames
.resize(frames
.size());
73 void CXbtFile::Close()
75 m_unpackedFrames
.clear();
77 m_positionWithinFrame
= 0;
79 m_frameStartPositions
.clear();
83 bool CXbtFile::Exists(const CURL
& url
)
86 return GetFile(url
, dummy
);
89 int64_t CXbtFile::GetPosition()
94 return m_positionTotal
;
97 int64_t CXbtFile::GetLength()
102 return static_cast<int>(m_xbtfFile
.GetUnpackedSize());
105 int CXbtFile::Stat(struct __stat64
*buffer
)
110 return Stat(m_url
, buffer
);
113 int CXbtFile::Stat(const CURL
& url
, struct __stat64
* buffer
)
120 // check if the file exists
121 CXBTFReaderPtr reader
;
123 if (!GetReaderAndFile(url
, reader
, file
))
125 // check if the URL points to the XBT file itself
126 if (!url
.GetFileName().empty() || !CFile::Exists(url
.GetHostName()))
129 // stat the XBT file itself
130 if (XFILE::CFile::Stat(url
.GetHostName(), buffer
) != 0)
133 buffer
->st_mode
= _S_IFDIR
;
137 // stat the XBT file itself
138 if (XFILE::CFile::Stat(url
.GetHostName(), buffer
) != 0)
141 buffer
->st_size
= file
.GetUnpackedSize();
146 ssize_t
CXbtFile::Read(void* lpBuf
, size_t uiBufSize
)
148 if (lpBuf
== nullptr || !m_open
)
152 if (m_xbtfFile
.GetFrames().empty() || m_positionTotal
>= GetLength())
155 // we can't read more than is left
156 if (static_cast<int64_t>(uiBufSize
) > GetLength() - m_positionTotal
)
157 uiBufSize
= static_cast<ssize_t
>(GetLength() - m_positionTotal
);
159 // we can't read more than we can signal with the return value
160 if (uiBufSize
> SSIZE_MAX
)
161 uiBufSize
= SSIZE_MAX
;
163 const auto& frames
= m_xbtfFile
.GetFrames();
165 size_t remaining
= uiBufSize
;
166 while (remaining
> 0)
168 const CXBTFFrame
& frame
= frames
[m_frameIndex
];
170 // check if we have already unpacked the current frame
171 if (m_unpackedFrames
[m_frameIndex
].empty())
173 // unpack the data from the current frame
174 std::vector
<uint8_t> unpackedFrame
=
175 CTextureBundleXBT::UnpackFrame(*m_xbtfReader
.get(), frame
);
176 if (unpackedFrame
.empty())
182 m_unpackedFrames
[m_frameIndex
] = std::move(unpackedFrame
);
185 // determine how many bytes we need to copy from the current frame
186 uint64_t remainingBytesInFrame
= frame
.GetUnpackedSize() - m_positionWithinFrame
;
187 size_t bytesToCopy
= remaining
;
188 if (remainingBytesInFrame
<= SIZE_MAX
)
189 bytesToCopy
= std::min(remaining
, static_cast<size_t>(remainingBytesInFrame
));
192 memcpy(lpBuf
, m_unpackedFrames
[m_frameIndex
].data() + m_positionWithinFrame
, bytesToCopy
);
193 m_positionWithinFrame
+= bytesToCopy
;
194 m_positionTotal
+= bytesToCopy
;
195 remaining
-= bytesToCopy
;
197 // check if we need to go to the next frame and there is a next frame
198 if (m_positionWithinFrame
>= frame
.GetUnpackedSize() && m_frameIndex
< frames
.size() - 1)
200 m_positionWithinFrame
= 0;
208 int64_t CXbtFile::Seek(int64_t iFilePosition
, int iWhence
)
213 int64_t newPosition
= m_positionTotal
;
217 newPosition
= iFilePosition
;
221 newPosition
+= iFilePosition
;
225 newPosition
= GetLength() + iFilePosition
;
228 // unsupported seek mode
233 // can't seek before the beginning or after the end of the file
234 if (newPosition
< 0 || newPosition
>= GetLength())
237 // seeking backwards doesn't require additional work
238 if (newPosition
<= m_positionTotal
)
240 m_positionTotal
= newPosition
;
241 return m_positionTotal
;
244 // when seeking forward we need to unpack all frames we seek past
245 const auto& frames
= m_xbtfFile
.GetFrames();
246 while (m_positionTotal
< newPosition
)
248 const CXBTFFrame
& frame
= frames
[m_frameIndex
];
250 // check if we have already unpacked the current frame
251 if (m_unpackedFrames
[m_frameIndex
].empty())
253 // unpack the data from the current frame
254 std::vector
<uint8_t> unpackedFrame
=
255 CTextureBundleXBT::UnpackFrame(*m_xbtfReader
.get(), frame
);
256 if (unpackedFrame
.empty())
262 m_unpackedFrames
[m_frameIndex
] = std::move(unpackedFrame
);
265 int64_t remainingBytesToSeek
= newPosition
- m_positionTotal
;
266 // check if the new position is within the current frame
267 uint64_t remainingBytesInFrame
= frame
.GetUnpackedSize() - m_positionWithinFrame
;
268 if (static_cast<uint64_t>(remainingBytesToSeek
) < remainingBytesInFrame
)
270 m_positionWithinFrame
+= remainingBytesToSeek
;
274 // otherwise move to the end of the frame
275 m_positionTotal
+= remainingBytesInFrame
;
276 m_positionWithinFrame
+= remainingBytesInFrame
;
278 // and go to the next frame if there is a next frame
279 if (m_frameIndex
< frames
.size() - 1)
281 m_positionWithinFrame
= 0;
286 m_positionTotal
= newPosition
;
287 return m_positionTotal
;
290 uint32_t CXbtFile::GetImageWidth() const
293 if (!GetFirstFrame(frame
))
296 return frame
.GetWidth();
299 uint32_t CXbtFile::GetImageHeight() const
302 if (!GetFirstFrame(frame
))
305 return frame
.GetHeight();
308 uint32_t CXbtFile::GetImageFormat() const
311 if (!GetFirstFrame(frame
))
314 return frame
.GetFormat();
317 bool CXbtFile::HasImageAlpha() const
320 if (!GetFirstFrame(frame
))
323 return frame
.HasAlpha();
326 bool CXbtFile::GetFirstFrame(CXBTFFrame
& frame
) const
331 const auto& frames
= m_xbtfFile
.GetFrames();
335 frame
= frames
.at(0);
339 bool CXbtFile::GetReader(const CURL
& url
, CXBTFReaderPtr
& reader
)
342 xbtUrl
.SetOptions("");
344 return CXbtManager::GetInstance().GetReader(xbtUrl
, reader
);
347 bool CXbtFile::GetReaderAndFile(const CURL
& url
, CXBTFReaderPtr
& reader
, CXBTFFile
& file
)
349 if (!GetReader(url
, reader
))
353 xbtUrl
.SetOptions("");
355 // CXBTFReader stores all filenames in lower case
356 std::string fileName
= xbtUrl
.GetFileName();
357 StringUtils::ToLower(fileName
);
359 return reader
->Get(fileName
, file
);
362 bool CXbtFile::GetFile(const CURL
& url
, CXBTFFile
& file
)
364 CXBTFReaderPtr reader
;
365 return GetReaderAndFile(url
, reader
, file
);