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