1 // license:BSD-3-Clause
2 // copyright-holders:Nathan Woods
3 /***************************************************************************
4
5 stream.cpp
6
7 Code for implementing Imgtool streams
8
9 ***************************************************************************/
10
11 #include <cstdio>
12 #include <cstring>
13 #include <zlib.h>
14
15 #include "unzip.h"
16 #include "osdcore.h"
17 #include "imgtool.h"
18
19
20 //-------------------------------------------------
21 // ctor
22 //-------------------------------------------------
23
stream(bool wp)24 imgtool::stream::stream(bool wp)
25 : imgtype(IMG_FILE)
26 , write_protect(wp)
27 , position(0)
28 , filesize(0)
29 , file()
30 , buffer(nullptr)
31 {
32 }
33
34
35 //-------------------------------------------------
36 // ctor
37 //-------------------------------------------------
38
stream(bool wp,util::core_file::ptr && f)39 imgtool::stream::stream(bool wp, util::core_file::ptr &&f)
40 : imgtype(IMG_FILE)
41 , write_protect(wp)
42 , position(0)
43 , filesize(f->size())
44 , file(std::move(f))
45 , buffer(nullptr)
46 {
47 }
48
49
50 //-------------------------------------------------
51 // ctor
52 //-------------------------------------------------
53
stream(bool wp,std::size_t size)54 imgtool::stream::stream(bool wp, std::size_t size)
55 : imgtype(IMG_MEM)
56 , write_protect(wp)
57 , position(0)
58 , filesize(size)
59 , file()
60 , buffer(reinterpret_cast<std::uint8_t *>(malloc(size)))
61 {
62 }
63
64
65 //-------------------------------------------------
66 // ctor
67 //-------------------------------------------------
68
stream(bool wp,std::size_t size,void * buf)69 imgtool::stream::stream(bool wp, std::size_t size, void *buf)
70 : imgtype(IMG_MEM)
71 , write_protect(wp)
72 , position(0)
73 , filesize(size)
74 , file()
75 , buffer(reinterpret_cast<std::uint8_t *>(buf))
76 {
77 }
78
79
80 //-------------------------------------------------
81 // dtor
82 //-------------------------------------------------
83
~stream()84 imgtool::stream::~stream()
85 {
86 if (buffer)
87 free(buffer);
88 }
89
90
91 //-------------------------------------------------
92 // open_zip
93 //-------------------------------------------------
94
open_zip(const std::string & zipname,const char * subname,int read_or_write)95 imgtool::stream::ptr imgtool::stream::open_zip(const std::string &zipname, const char *subname, int read_or_write)
96 {
97 if (read_or_write)
98 return imgtool::stream::ptr();
99
100 /* check to see if the file exists */
101 FILE *f = fopen(zipname.c_str(), "r");
102 if (!f)
103 return imgtool::stream::ptr();
104 fclose(f);
105
106 imgtool::stream::ptr imgfile(new imgtool::stream(true));
107
108 imgfile->imgtype = IMG_MEM;
109
110 util::archive_file::ptr z;
111 util::archive_file::open_zip(zipname, z);
112 if (!z)
113 return imgtool::stream::ptr();
114
115 int zipent = z->first_file();
116 while ((zipent >= 0) && subname && strcmp(subname, z->current_name().c_str()))
117 zipent = z->next_file();
118 if (zipent < 0)
119 return imgtool::stream::ptr();
120
121 imgfile->filesize = z->current_uncompressed_length();
122 imgfile->buffer = reinterpret_cast<std::uint8_t *>(malloc(z->current_uncompressed_length()));
123 if (!imgfile->buffer)
124 return imgtool::stream::ptr();
125
126 if (z->decompress(imgfile->buffer, z->current_uncompressed_length()) != util::archive_file::error::NONE)
127 return imgtool::stream::ptr();
128
129 return imgfile;
130 }
131
132
133
134 //-------------------------------------------------
135 // open
136 //-------------------------------------------------
137
open(const std::string & filename,int read_or_write)138 imgtool::stream::ptr imgtool::stream::open(const std::string &filename, int read_or_write)
139 {
140 static const uint32_t write_modes[] =
141 {
142 OPEN_FLAG_READ,
143 OPEN_FLAG_WRITE | OPEN_FLAG_CREATE,
144 OPEN_FLAG_READ | OPEN_FLAG_WRITE,
145 OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE
146 };
147 imgtool::stream::ptr s;
148 char c;
149
150 // maybe we are just a ZIP?
151 std::string ext = core_filename_extract_extension(filename);
152 if (!core_stricmp(ext.c_str(), ".zip"))
153 return open_zip(filename, nullptr, read_or_write);
154
155 util::core_file::ptr f = nullptr;
156 auto const filerr = util::core_file::open(filename, write_modes[read_or_write], f);
157 if (filerr != osd_file::error::NONE)
158 {
159 if (!read_or_write)
160 {
161 int const len = filename.size();
162
163 /* can't open the file; try opening ZIP files with other names */
164 std::vector<char> buf(len + 1);
165 strcpy(&buf[0], filename.c_str());
166
167 for (int i = len-1; !s && (i >= 0); i--)
168 {
169 if ((buf[i] == '\\') || (buf[i] == '/'))
170 {
171 c = buf[i];
172 buf[i] = '\0';
173 s = open_zip(&buf[0], &buf[i + 1], read_or_write);
174 buf[i] = c;
175 }
176 }
177
178 if (s)
179 return s;
180 }
181
182 /* ah well, it was worth a shot */
183 return imgtool::stream::ptr();
184 }
185
186 imgtool::stream::ptr imgfile(new imgtool::stream(read_or_write ? false : true, std::move(f)));
187
188 // normal file
189 return imgfile;
190 }
191
192
193 //-------------------------------------------------
194 // open_write_stream
195 //-------------------------------------------------
196
open_write_stream(int size)197 imgtool::stream::ptr imgtool::stream::open_write_stream(int size)
198 {
199 imgtool::stream::ptr imgfile(new imgtool::stream(false, size));
200 if (!imgfile->buffer)
201 return imgtool::stream::ptr();
202
203 return imgfile;
204 }
205
206
207 //-------------------------------------------------
208 // open_mem
209 //-------------------------------------------------
210
open_mem(void * buf,size_t sz)211 imgtool::stream::ptr imgtool::stream::open_mem(void *buf, size_t sz)
212 {
213 imgtool::stream::ptr imgfile(new imgtool::stream(false, sz, buf));
214
215 return imgfile;
216 }
217
218
219 //-------------------------------------------------
220 // core_file
221 //-------------------------------------------------
222
core_file()223 util::core_file *imgtool::stream::core_file()
224 {
225 return (imgtype == IMG_FILE) ? file.get() : nullptr;
226 }
227
228
229 //-------------------------------------------------
230 // read
231 //-------------------------------------------------
232
read(void * buf,uint32_t sz)233 uint32_t imgtool::stream::read(void *buf, uint32_t sz)
234 {
235 uint32_t result = 0;
236
237 switch(imgtype)
238 {
239 case IMG_FILE:
240 assert(sz == (uint32_t) sz);
241 file->seek(position, SEEK_SET);
242 result = file->read(buf, (uint32_t) sz);
243 break;
244
245 case IMG_MEM:
246 /* do we have to limit sz? */
247 if (sz > (filesize - position))
248 sz = (uint32_t) (filesize - position);
249
250 memcpy(buf, buffer + position, sz);
251 result = sz;
252 break;
253
254 default:
255 assert(0);
256 break;
257 }
258 position += result;
259 return result;
260 }
261
262
263 //-------------------------------------------------
264 // write
265 //-------------------------------------------------
266
write(const void * buf,uint32_t sz)267 uint32_t imgtool::stream::write(const void *buf, uint32_t sz)
268 {
269 void *new_buffer;
270 uint32_t result = 0;
271
272 switch(imgtype)
273 {
274 case IMG_MEM:
275 if (!write_protect)
276 {
277 /* do we have to expand the buffer? */
278 if (filesize < position + sz)
279 {
280 /* try to expand the buffer */
281 new_buffer = realloc(buffer , position + sz);
282 if (new_buffer)
283 {
284 buffer = (uint8_t*)new_buffer;
285 filesize = position + sz;
286 }
287 }
288
289 /* do we have to limit sz? */
290 if (sz > (filesize - position))
291 sz = (uint32_t) (filesize - position);
292
293 memcpy(buffer + position, buf, sz);
294 result = sz;
295 }
296 break;
297
298 case IMG_FILE:
299 file->seek(position, SEEK_SET);
300 result = file->write(buf, sz);
301 break;
302
303 default:
304 assert(0);
305 break;
306 }
307
308 /* advance the file pointer */
309 position += result;
310
311 /* did we grow the file */
312 if (position > filesize)
313 filesize = position;
314 return result;
315 }
316
317
318 //-------------------------------------------------
319 // size
320 //-------------------------------------------------
321
size() const322 uint64_t imgtool::stream::size() const
323 {
324 return filesize;
325 }
326
327
328 //-------------------------------------------------
329 // getptr
330 //-------------------------------------------------
331
getptr()332 void *imgtool::stream::getptr()
333 {
334 void *ptr;
335
336 switch(imgtype)
337 {
338 case IMG_MEM:
339 ptr = buffer;
340 break;
341
342 default:
343 ptr = nullptr;
344 break;
345 }
346 return ptr;
347 }
348
349
350 //-------------------------------------------------
351 // seek
352 //-------------------------------------------------
353
seek(int64_t pos,int where)354 int imgtool::stream::seek(int64_t pos, int where)
355 {
356 switch(where)
357 {
358 case SEEK_CUR:
359 pos += position;
360 break;
361 case SEEK_END:
362 pos += size();
363 break;
364 }
365
366 if (pos < 0)
367 position = 0;
368 else
369 position = std::min(size(), uint64_t(pos));
370
371 if (position < pos)
372 fill('\0', pos - position);
373
374 return 0;
375 }
376
377
378 //-------------------------------------------------
379 // tell
380 //-------------------------------------------------
381
tell()382 uint64_t imgtool::stream::tell()
383 {
384 return position;
385 }
386
387
388 //-------------------------------------------------
389 // transfer
390 //-------------------------------------------------
391
transfer(imgtool::stream & dest,imgtool::stream & source,uint64_t sz)392 uint64_t imgtool::stream::transfer(imgtool::stream &dest, imgtool::stream &source, uint64_t sz)
393 {
394 uint64_t result = 0;
395 uint64_t readsz;
396 char buf[1024];
397
398 while(sz && (readsz = source.read(buf, std::min(sz, uint64_t(sizeof(buf))))))
399 {
400 dest.write(buf, readsz);
401 sz -= readsz;
402 result += readsz;
403 }
404 return result;
405 }
406
407
408 //-------------------------------------------------
409 // transfer_all
410 //-------------------------------------------------
411
transfer_all(imgtool::stream & dest,imgtool::stream & source)412 uint64_t imgtool::stream::transfer_all(imgtool::stream &dest, imgtool::stream &source)
413 {
414 return transfer(dest, source, source.size());
415 }
416
417
418 //-------------------------------------------------
419 // crc
420 //-------------------------------------------------
421
crc(unsigned long * result)422 int imgtool::stream::crc(unsigned long *result)
423 {
424 size_t sz;
425 void *ptr;
426
427 switch(imgtype)
428 {
429 case IMG_MEM:
430 *result = crc32(0, (unsigned char *) buffer, (size_t) filesize);
431 break;
432
433 default:
434 sz = size();
435 ptr = malloc(sz);
436 if (!ptr)
437 return IMGTOOLERR_OUTOFMEMORY;
438 seek(0, SEEK_SET);
439 if (read(ptr, sz) != sz)
440 {
441 free(ptr);
442 return IMGTOOLERR_READERROR;
443 }
444 *result = crc32(0, (const Bytef*)ptr, sz);
445 free(ptr);
446 break;
447 }
448 return 0;
449 }
450
451
452 //-------------------------------------------------
453 // file_crc
454 //-------------------------------------------------
455
file_crc(const char * fname,unsigned long * result)456 int imgtool::stream::file_crc(const char *fname, unsigned long *result)
457 {
458 int err;
459 imgtool::stream::ptr f;
460
461 f = imgtool::stream::open(fname, OSD_FOPEN_READ);
462 if (!f)
463 return IMGTOOLERR_FILENOTFOUND;
464
465 err = f->crc(result);
466 return err;
467 }
468
469
470 //-------------------------------------------------
471 // fill
472 //-------------------------------------------------
473
fill(unsigned char b,uint64_t sz)474 uint64_t imgtool::stream::fill(unsigned char b, uint64_t sz)
475 {
476 uint64_t outsz;
477 char buf[1024];
478
479 outsz = 0;
480 memset(buf, b, (std::min<uint64_t>)(sz, sizeof(buf)));
481
482 while (sz)
483 {
484 outsz += write(buf, (std::min<uint64_t>)(sz, sizeof(buf)));
485 sz -= (std::min<uint64_t>)(sz, sizeof(buf));
486 }
487 return outsz;
488 }
489
490
491 //-------------------------------------------------
492 // is_read_only
493 //-------------------------------------------------
494
is_read_only()495 bool imgtool::stream::is_read_only()
496 {
497 return write_protect;
498 }
499
500
501 //-------------------------------------------------
502 // putc
503 //-------------------------------------------------
504
putc(char c)505 uint32_t imgtool::stream::putc(char c)
506 {
507 return write(&c, 1);
508 }
509
510
511 //-------------------------------------------------
512 // puts
513 //-------------------------------------------------
514
puts(const char * s)515 uint32_t imgtool::stream::puts(const char *s)
516 {
517 return write(s, strlen(s));
518 }
519
520
521 //-------------------------------------------------
522 // printf
523 //-------------------------------------------------
524
printf(const char * fmt,...)525 uint32_t imgtool::stream::printf(const char *fmt, ...)
526 {
527 va_list va;
528 char buf[256];
529
530 va_start(va, fmt);
531 vsprintf(buf, fmt, va);
532 va_end(va);
533
534 return puts(buf);
535 }
536