1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 14 янв. 2018 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <dsp/endian.h>
23 #include <core/stdlib/string.h>
24 #include <core/files/lspc/lspc.h>
25 #include <core/files/lspc/LSPCChunkReader.h>
26 
27 namespace lsp
28 {
29 
LSPCChunkReader(LSPCResource * fd,uint32_t magic,uint32_t uid)30     LSPCChunkReader::LSPCChunkReader(LSPCResource *fd, uint32_t magic, uint32_t uid):
31         LSPCChunkAccessor(fd, magic)
32     {
33         nUnread     = 0;
34         nBufTail    = 0;
35         nFileOff    = 0;
36         nUID        = uid;
37         bLast       = false;
38     }
39 
~LSPCChunkReader()40     LSPCChunkReader::~LSPCChunkReader()
41     {
42     }
43 
read(void * buf,size_t count)44     ssize_t LSPCChunkReader::read(void *buf, size_t count)
45     {
46         if (pFile == NULL)
47             return -set_error(STATUS_CLOSED);
48 
49         lspc_chunk_header_t hdr;
50 
51         uint8_t *dst        = static_cast<uint8_t *>(buf);
52         ssize_t total       = 0;
53 
54         while (count > 0)
55         {
56             size_t to_read = nBufTail - nBufPos;
57 
58             if (to_read > 0) // There is data in the buffer?
59             {
60                 if (to_read > count)
61                     to_read     = count;
62 
63                 // Copy memory from buffer
64                 memcpy(dst, &pBuffer[nBufPos], to_read);
65 
66                 // Update pointer
67                 dst        += to_read;
68                 nBufPos    += to_read;
69                 count      -= to_read;
70                 total      += to_read;
71             }
72             else if (nUnread > 0)
73             {
74                 if (nUnread <= count)
75                 {
76                     // Read data
77                     ssize_t n   = pFile->read(nFileOff, dst, nUnread);
78                     if (n <= 0)
79                         return total;
80 
81                     // Update pointer
82                     dst        += n;
83                     count      -= n;
84                     total      += n;
85                     nUnread    -= n;
86                     nFileOff   += n;
87                 }
88                 else // Fill buffer
89                 {
90                     to_read     = (nUnread < nBufSize) ? nUnread : nBufSize;
91 
92                     // Read data
93                     ssize_t n   = pFile->read(nFileOff, pBuffer, to_read);
94                     if (n <= 0)
95                         return total;
96 
97                     // Update pointer
98                     nBufPos     = 0;
99                     nBufTail    = n;
100                     nFileOff   += n;
101                     nUnread    -= n;
102                 }
103             }
104             else // Seek for the next valid chunk
105             {
106                 // There is no chunk after current
107                 if (bLast)
108                 {
109                     set_error(STATUS_EOF);
110                     return total;
111                 }
112 
113                 // Read chunk header
114                 ssize_t n   = pFile->read(nFileOff, &hdr, sizeof(lspc_chunk_header_t));
115                 if (n < ssize_t(sizeof(lspc_chunk_header_t)))
116                 {
117                     set_error(STATUS_EOF);
118                     return total;
119                 }
120                 nFileOff   += sizeof(lspc_chunk_header_t);
121 
122                 hdr.magic       = BE_TO_CPU(hdr.magic);
123                 hdr.flags       = BE_TO_CPU(hdr.flags);
124                 hdr.size        = BE_TO_CPU(hdr.size);
125                 hdr.uid         = BE_TO_CPU(hdr.uid);
126 
127                 // Validate chunk header
128                 if ((hdr.magic == nMagic) && (hdr.uid == nUID)) // We've found our chunk, remember unread bytes count
129                 {
130                     bLast           = hdr.flags & LSPC_CHUNK_FLAG_LAST;
131                     nUnread         = hdr.size;
132                 }
133                 else // Skip this chunk
134                     nFileOff       += hdr.size;
135             }
136         }
137 
138         return total;
139     }
140 
read_header(void * hdr,size_t size)141     ssize_t LSPCChunkReader::read_header(void *hdr, size_t size)
142     {
143         if (size < sizeof(lspc_header_t))
144             return -set_error(STATUS_BAD_ARGUMENTS);
145 
146         // Read header data first
147         lspc_header_t shdr;
148         ssize_t count   = read(&shdr, sizeof(lspc_header_t));
149         if (count < 0)
150             return count;
151         else if (count < ssize_t(sizeof(lspc_header_t)))
152             return -set_error(STATUS_EOF); // Unexpected end of file
153 
154         // Now read header
155         lspc_chunk_raw_header_t *dhdr = reinterpret_cast<lspc_chunk_raw_header_t *>(hdr);
156         size_t hdr_size             = BE_TO_CPU(shdr.size);
157         if (hdr_size < sizeof(lspc_header_t)) // header size should be at least of sizeof(lspc_header_t)
158             return -set_error(STATUS_CORRUPTED_FILE);
159         dhdr->common.size           = hdr_size;
160         dhdr->common.version        = BE_TO_CPU(shdr.version);
161         hdr_size                   -= sizeof(lspc_header_t);
162         size                       -= sizeof(lspc_header_t);
163 
164         // Read header contents
165         ssize_t to_read = (size > hdr_size) ? hdr_size : size;
166         count           = read(&dhdr->data, to_read);
167         if (count < 0)
168             return count;
169         else if (count < to_read)
170             return -set_error(STATUS_EOF); // Unexpected end of file
171 
172         // Analyze size of header
173         if (size < hdr_size) // Requested size less than actual header size?
174         {
175             // We need to skip extra bytes that do not fit into header
176             to_read     = hdr_size - size;
177             count       = skip(to_read);
178             if (count < 0)
179                 return count;
180             else if (count < to_read)
181                 return -set_error(STATUS_EOF); // Unexpected end of file
182 
183             // Patch the header size to be at most of size bytes
184             dhdr->common.size           = size + sizeof(lspc_header_t);
185         }
186         else if (size > hdr_size)
187             bzero(&dhdr->data[count], size - hdr_size);
188 
189         return dhdr->common.size;
190     }
191 
skip(size_t count)192     ssize_t LSPCChunkReader::skip(size_t count)
193     {
194         if (pFile == NULL)
195             return -set_error(STATUS_CLOSED);
196 
197         lspc_chunk_header_t hdr;
198 
199         ssize_t total       = 0;
200 
201         while (count > 0)
202         {
203             size_t to_read = nBufTail - nBufPos;
204 
205             if (to_read > 0) // There is data in the buffer?
206             {
207                 if (to_read > count)
208                     to_read     = count;
209 
210                 // Update pointer
211                 nBufPos    += to_read;
212                 count      -= to_read;
213                 total      += to_read;
214             }
215             else if (nUnread > 0)
216             {
217                 if (nUnread <= count)
218                 {
219                     // Update counters
220                     count      -= nUnread;
221                     total      += nUnread;
222                     nFileOff   += nUnread;
223                     nUnread     = 0;
224                 }
225                 else // Fill buffer
226                 {
227                     nUnread    -= count;
228                     nFileOff   += count;
229                     total      += count;
230                     count       = 0;
231                 }
232             }
233             else // Seek for the next valid chunk
234             {
235                 // There is no chunk after current
236                 if (bLast)
237                 {
238                     set_error(STATUS_EOF);
239                     return total;
240                 }
241 
242                 // Read chunk header
243                 ssize_t n   = pFile->read(nFileOff, &hdr, sizeof(lspc_chunk_header_t));
244                 if (n < ssize_t(sizeof(lspc_chunk_header_t)))
245                 {
246                     set_error(STATUS_EOF);
247                     return 0;
248                 }
249                 nFileOff   += sizeof(lspc_chunk_header_t);
250 
251                 hdr.magic       = BE_TO_CPU(hdr.magic);
252                 hdr.flags       = BE_TO_CPU(hdr.flags);
253                 hdr.size        = BE_TO_CPU(hdr.size);
254                 hdr.uid         = BE_TO_CPU(hdr.uid);
255 
256                 // Validate chunk header
257                 if ((hdr.magic == nMagic) && (hdr.uid == nUID)) // We've found our chunk, remember unread bytes count
258                 {
259                     bLast           = hdr.flags & LSPC_CHUNK_FLAG_LAST;
260                     nUnread         = hdr.size;
261                 }
262                 else // Skip this chunk
263                     nFileOff       += hdr.size;
264             }
265         }
266 
267         return total;
268     }
269 
270 } /* namespace lsp */
271