1 // license:BSD-3-Clause
2 // copyright-holders:Tim Schuerewegen
3 /*
4 
5     Cybiko Xtreme File System
6 
7     (c) 2010 Tim Schuerewegen
8 
9 */
10 
11 #include "imgtool.h"
12 
13 #include <zlib.h>
14 
15 struct cybiko_file_system
16 {
17 	imgtool::stream *stream;
18 	uint32_t page_count, page_size, block_count_boot, block_count_file;
19 	uint16_t write_count;
20 };
21 
22 struct cybiko_iter
23 {
24 	uint16_t block;
25 };
26 
27 struct cfs_file
28 {
29 	char name[64]; // name of the file
30 	uint32_t date;   // date/time of the file (seconds since 1900/01/01)
31 	uint32_t size;   // size of the file
32 	uint32_t blocks; // number of blocks occupied by the file
33 };
34 
35 enum
36 {
37 	BLOCK_TYPE_INVALID,
38 	BLOCK_TYPE_BOOT,
39 	BLOCK_TYPE_FILE
40 };
41 
42 #define MAX_PAGE_SIZE (264 * 2)
43 
44 #define INVALID_FILE_ID  0xFFFF
45 
46 #define BLOCK_USED(x)      (x[0] & 0x80)
47 #define BLOCK_FILE_ID(x)   buffer_read_16_be( x + 2)
48 #define BLOCK_PART_ID(x)   buffer_read_16_be( x + 4)
49 #define BLOCK_FILENAME(x)  (char*)(x + 7)
50 
51 #define FILE_HEADER_SIZE  0x48
52 
get_cfs(imgtool::image & image)53 static cybiko_file_system *get_cfs(imgtool::image &image)
54 {
55 	return (cybiko_file_system*)image.extra_bytes();
56 }
57 
58 extern imgtool::datetime cybiko_time_crack(uint32_t cfs_time);
59 extern uint32_t cybiko_time_setup(const imgtool::datetime &t);
60 
buffer_read_32_be(uint8_t * buffer)61 static uint32_t buffer_read_32_be( uint8_t *buffer)
62 {
63 	return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3] << 0);
64 }
65 
buffer_read_16_be(uint8_t * buffer)66 static uint16_t buffer_read_16_be( uint8_t *buffer)
67 {
68 	return (buffer[0] << 8) | (buffer[1] << 0);
69 }
70 
buffer_write_32_be(uint8_t * buffer,uint32_t data)71 static void buffer_write_32_be( uint8_t *buffer, uint32_t data)
72 {
73 	buffer[0] = (data >> 24) & 0xFF;
74 	buffer[1] = (data >> 16) & 0xFF;
75 	buffer[2] = (data >>  8) & 0xFF;
76 	buffer[3] = (data >>  0) & 0xFF;
77 }
78 
buffer_write_16_be(uint8_t * buffer,uint16_t data)79 static void buffer_write_16_be( uint8_t *buffer, uint16_t data)
80 {
81 	buffer[0] = (data >> 8) & 0xFF;
82 	buffer[1] = (data >> 0) & 0xFF;
83 }
84 
85 // page = crc (2) + data (x)
86 
page_buffer_calc_checksum(uint8_t * data,uint32_t size)87 static uint16_t page_buffer_calc_checksum( uint8_t *data, uint32_t size)
88 {
89 	int i;
90 	uint32_t val = 0;
91 	for (i = 0; i < size; i++)
92 	{
93 		val = (val ^ data[i] ^ i) << 1;
94 		val = val | ((val >> 16) & 0x0001);
95 	}
96 	return val;
97 }
98 
page_buffer_verify(uint8_t * buffer,uint32_t size,int block_type)99 static int page_buffer_verify( uint8_t *buffer, uint32_t size, int block_type)
100 {
101 	// checksum
102 	if (block_type == BLOCK_TYPE_FILE)
103 	{
104 		uint32_t checksum_page, checksum_calc;
105 		checksum_calc = page_buffer_calc_checksum( buffer + 2, size - 2);
106 		checksum_page = buffer_read_16_be( buffer + 0);
107 		if (checksum_calc != checksum_page) return false;
108 	}
109 	// ok
110 	return true;
111 }
112 
cfs_block_to_page(cybiko_file_system * cfs,int block_type,uint32_t block,uint32_t * page)113 static int cfs_block_to_page( cybiko_file_system *cfs, int block_type, uint32_t block, uint32_t *page)
114 {
115 	switch (block_type)
116 	{
117 		case BLOCK_TYPE_BOOT : if (page) *page = block; return true;
118 		case BLOCK_TYPE_FILE : if (page) *page = block + cfs->block_count_boot; return true;
119 		default              : return false;
120 	}
121 }
122 
cfs_page_to_block(cybiko_file_system * cfs,uint32_t page,int * block_type,uint32_t * block)123 static int cfs_page_to_block( cybiko_file_system *cfs, uint32_t page, int *block_type, uint32_t *block)
124 {
125 	uint32_t tmp = page;
126 	// boot block
127 	if (tmp < cfs->block_count_boot)
128 	{
129 		if (block_type) *block_type = BLOCK_TYPE_BOOT;
130 		if (block) *block = tmp;
131 		return true;
132 	}
133 	tmp -= cfs->block_count_boot;
134 	// file block
135 	if (tmp < cfs->block_count_file)
136 	{
137 		if (block_type) *block_type = BLOCK_TYPE_FILE;
138 		if (block) *block = tmp;
139 		return true;
140 	}
141 	tmp -= cfs->block_count_file;
142 	// error
143 	return false;
144 }
145 
cfs_page_read(cybiko_file_system * cfs,uint8_t * buffer,uint32_t page)146 static int cfs_page_read( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page)
147 {
148 	if (page >= cfs->page_count) return false;
149 	cfs->stream->seek(page * cfs->page_size, SEEK_SET);
150 	cfs->stream->read(buffer, cfs->page_size);
151 	return true;
152 }
153 
cfs_page_write(cybiko_file_system * cfs,uint8_t * buffer,uint32_t page)154 static int cfs_page_write( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page)
155 {
156 	if (page >= cfs->page_count) return false;
157 	cfs->stream->seek(page * cfs->page_size, SEEK_SET);
158 	cfs->stream->write(buffer, cfs->page_size);
159 	return true;
160 }
161 
cfs_block_read(cybiko_file_system * cfs,uint8_t * buffer,int block_type,uint32_t block)162 static int cfs_block_read( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block)
163 {
164 	uint8_t buffer_page[MAX_PAGE_SIZE];
165 	uint32_t page;
166 	if (!cfs_block_to_page( cfs, block_type, block, &page)) return false;
167 	if (!cfs_page_read( cfs, buffer_page, page)) return false;
168 	memcpy( buffer, buffer_page + 2, cfs->page_size - 2);
169 	return true;
170 }
171 
cfs_block_write(cybiko_file_system * cfs,uint8_t * buffer,int block_type,uint32_t block)172 static int cfs_block_write( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block)
173 {
174 	uint8_t buffer_page[MAX_PAGE_SIZE];
175 	uint32_t page;
176 	uint16_t checksum;
177 	memcpy( buffer_page + 2, buffer, cfs->page_size - 2);
178 	if (block_type == BLOCK_TYPE_BOOT)
179 	{
180 		checksum = 0xFFFF;
181 	}
182 	else
183 	{
184 		checksum = page_buffer_calc_checksum( buffer_page + 2, cfs->page_size - 2);
185 	}
186 	buffer_write_16_be( buffer_page + 0, checksum);
187 	if (!cfs_block_to_page( cfs, block_type, block, &page)) return false;
188 	if (!cfs_page_write( cfs, buffer_page, page)) return false;
189 	return true;
190 }
191 
cfs_file_delete(cybiko_file_system * cfs,uint16_t file_id)192 static int cfs_file_delete( cybiko_file_system *cfs, uint16_t file_id)
193 {
194 	uint8_t buffer[MAX_PAGE_SIZE];
195 	int i;
196 	for (i=0;i<cfs->block_count_file;i++)
197 	{
198 		if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
199 		if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id))
200 		{
201 			buffer[0] &= ~0x80;
202 			if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
203 		}
204 	}
205 	return true;
206 }
207 
cfs_file_info(cybiko_file_system * cfs,uint16_t file_id,cfs_file * file)208 static int cfs_file_info( cybiko_file_system *cfs, uint16_t file_id, cfs_file *file)
209 {
210 	uint8_t buffer[MAX_PAGE_SIZE];
211 	int i;
212 	file->blocks = file->size = 0;
213 	for (i=0;i<cfs->block_count_file;i++)
214 	{
215 		if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
216 		if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id))
217 		{
218 			if (BLOCK_PART_ID(buffer) == 0)
219 			{
220 				strcpy( file->name, BLOCK_FILENAME(buffer));
221 				file->date = buffer_read_32_be( buffer + 6 + FILE_HEADER_SIZE - 4);
222 			}
223 			file->size += buffer[1];
224 			file->blocks++;
225 		}
226 	}
227 	return (file->blocks > 0) ? true : false;
228 }
229 
cfs_file_find(cybiko_file_system * cfs,const char * filename,uint16_t * file_id)230 static int cfs_file_find( cybiko_file_system *cfs, const char *filename, uint16_t *file_id)
231 {
232 	uint8_t buffer[MAX_PAGE_SIZE];
233 	int i;
234 	for (i=0;i<cfs->block_count_file;i++)
235 	{
236 		if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
237 		if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0) && (strcmp( filename, BLOCK_FILENAME(buffer)) == 0))
238 		{
239 			*file_id = i;
240 			return true;
241 		}
242 	}
243 	return false;
244 }
245 
cfs_verify(cybiko_file_system & cfs)246 static bool cfs_verify(cybiko_file_system &cfs)
247 {
248 	uint8_t buffer[MAX_PAGE_SIZE];
249 	int i, block_type;
250 	for (i = 0; i < cfs.page_count; i++)
251 	{
252 		if (!cfs_page_read(&cfs, buffer, i)) return false;
253 		if (!cfs_page_to_block(&cfs, i, &block_type, NULL)) return false;
254 		if (!page_buffer_verify(buffer, cfs.page_size, block_type)) return false;
255 	}
256 	return true;
257 }
258 
cfs_init(cybiko_file_system & cfs,imgtool::stream::ptr && stream)259 static bool cfs_init(cybiko_file_system &cfs, imgtool::stream::ptr &&stream)
260 {
261 	cfs.stream = stream.release();
262 	cfs.page_count = 2005;
263 	cfs.page_size = 258;
264 	cfs.block_count_boot = 5;
265 	cfs.block_count_file = cfs.page_count - cfs.block_count_boot;
266 	cfs.write_count = 0;
267 	return true;
268 }
269 
cfs_format(cybiko_file_system * cfs)270 static int cfs_format(cybiko_file_system *cfs)
271 {
272 	uint8_t buffer[MAX_PAGE_SIZE];
273 	int i;
274 	// boot blocks
275 	memset( buffer, 0xFF, sizeof( buffer));
276 	for (i=0;i<cfs->block_count_boot;i++)
277 	{
278 		if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_BOOT, i)) return false;
279 	}
280 	// file blocks
281 	memset( buffer, 0xFF, sizeof( buffer));
282 	buffer[0] &= ~0x80;
283 	for (i=0;i<cfs->block_count_file;i++)
284 	{
285 		if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
286 	}
287 	// padding
288 	buffer[0] = 0xFF;
289 	for (i=0;i<0x1B56;i++)
290 	{
291 		cfs->stream->write(buffer, 1);
292 	}
293 	// ok
294 	return true;
295 }
296 
cfs_calc_free_blocks(cybiko_file_system * cfs)297 static uint16_t cfs_calc_free_blocks( cybiko_file_system *cfs)
298 {
299 	uint8_t buffer[MAX_PAGE_SIZE];
300 	int i;
301 	uint16_t blocks = 0;
302 	for (i=0;i<cfs->block_count_file;i++)
303 	{
304 		if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return 0;
305 		if (!BLOCK_USED(buffer)) blocks++;
306 	}
307 	return blocks;
308 }
309 
cfs_calc_free_space(cybiko_file_system * cfs,uint16_t blocks)310 static uint32_t cfs_calc_free_space( cybiko_file_system *cfs, uint16_t blocks)
311 {
312 	uint32_t free_space;
313 	free_space = blocks * ((cfs->page_size - 2) - 6);
314 	if (free_space > 0) free_space -= FILE_HEADER_SIZE;
315 	return free_space;
316 }
317 
cybiko_image_open(imgtool::image & image,imgtool::stream::ptr && stream)318 static imgtoolerr_t cybiko_image_open(imgtool::image &image, imgtool::stream::ptr &&stream)
319 {
320 	cybiko_file_system *cfs = get_cfs(image);
321 	// init
322 	if (!cfs_init(*cfs, std::move(stream))) return IMGTOOLERR_CORRUPTIMAGE;
323 	// verify
324 	if (!cfs_verify(*cfs)) return IMGTOOLERR_CORRUPTIMAGE;
325 	// ok
326 	return IMGTOOLERR_SUCCESS;
327 }
328 
cybiko_image_close(imgtool::image & image)329 static void cybiko_image_close(imgtool::image &image)
330 {
331 	cybiko_file_system *cfs = get_cfs(image);
332 	delete cfs->stream;
333 }
334 
cybiko_image_create(imgtool::image & image,imgtool::stream::ptr && stream,util::option_resolution * opts)335 static imgtoolerr_t cybiko_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts)
336 {
337 	cybiko_file_system *cfs = get_cfs(image);
338 	// init
339 	if (!cfs_init(*cfs, std::move(stream))) return IMGTOOLERR_CORRUPTIMAGE;
340 	// format
341 	if (!cfs_format(cfs)) return IMGTOOLERR_CORRUPTIMAGE;
342 	// ok
343 	return IMGTOOLERR_SUCCESS;
344 }
345 
cybiko_image_begin_enum(imgtool::directory & enumeration,const char * path)346 static imgtoolerr_t cybiko_image_begin_enum(imgtool::directory &enumeration, const char *path)
347 {
348 	cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes();
349 	iter->block = 0;
350 	return IMGTOOLERR_SUCCESS;
351 }
352 
cybiko_image_next_enum(imgtool::directory & enumeration,imgtool_dirent & ent)353 static imgtoolerr_t cybiko_image_next_enum(imgtool::directory &enumeration, imgtool_dirent &ent)
354 {
355 	imgtool::image &image(enumeration.image());
356 	cybiko_file_system *cfs = get_cfs(image);
357 	cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes();
358 	uint8_t buffer[MAX_PAGE_SIZE];
359 	uint16_t file_id = INVALID_FILE_ID;
360 	cfs_file file;
361 	// find next file
362 	while (iter->block < cfs->block_count_file)
363 	{
364 		if (!cfs_block_read(cfs, buffer, BLOCK_TYPE_FILE, iter->block++)) return IMGTOOLERR_READERROR;
365 		if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0))
366 		{
367 			file_id = BLOCK_FILE_ID(buffer);
368 			break;
369 		}
370 	}
371 	// get file information
372 	if ((file_id != INVALID_FILE_ID) && cfs_file_info(cfs, file_id, &file))
373 	{
374 		strcpy(ent.filename, file.name);
375 		ent.filesize = file.size;
376 		ent.lastmodified_time = cybiko_time_crack(file.date);
377 		ent.filesize = file.size;
378 	}
379 	else
380 	{
381 		ent.eof = 1;
382 	}
383 	// ok
384 	return IMGTOOLERR_SUCCESS;
385 }
386 
cybiko_image_free_space(imgtool::partition & partition,uint64_t * size)387 static imgtoolerr_t cybiko_image_free_space(imgtool::partition &partition, uint64_t *size)
388 {
389 	imgtool::image &image(partition.image());
390 	cybiko_file_system *cfs = get_cfs(image);
391 	if (size) *size = cfs_calc_free_space( cfs, cfs_calc_free_blocks( cfs));
392 	return IMGTOOLERR_SUCCESS;
393 }
394 
cybiko_image_read_file(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & destf)395 static imgtoolerr_t cybiko_image_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
396 {
397 	imgtool::image &image(partition.image());
398 	cybiko_file_system *cfs = get_cfs(image);
399 	uint8_t buffer[MAX_PAGE_SIZE];
400 	uint16_t file_id, part_id = 0, old_part_id;
401 	int i;
402 	// check filename
403 	if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME;
404 	// find file
405 	if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND;
406 	// read file
407 	do
408 	{
409 		old_part_id = part_id;
410 		for (i=0;i<cfs->block_count_file;i++)
411 		{
412 			if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR;
413 			if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id) && (BLOCK_PART_ID(buffer) == part_id))
414 			{
415 				destf.write(buffer + 6 + ((part_id == 0) ? FILE_HEADER_SIZE : 0), buffer[1]);
416 				part_id++;
417 			}
418 		}
419 	} while (old_part_id != part_id);
420 	// ok
421 	return IMGTOOLERR_SUCCESS;
422 }
423 
cybiko_image_write_file(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & sourcef,util::option_resolution * opts)424 static imgtoolerr_t cybiko_image_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
425 {
426 	imgtool::image &image(partition.image());
427 	cybiko_file_system *cfs = get_cfs(image);
428 	uint8_t buffer[MAX_PAGE_SIZE];
429 	uint16_t file_id, part_id = 0, free_blocks;
430 	uint64_t bytes_left;
431 	cfs_file file;
432 	int i;
433 	// check filename
434 	if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME;
435 	// find file
436 	if (!cfs_file_find( cfs, filename, &file_id)) file_id = INVALID_FILE_ID;
437 	// check free space
438 	free_blocks = cfs_calc_free_blocks(cfs);
439 	if (file_id != INVALID_FILE_ID)
440 	{
441 		if (!cfs_file_info(cfs, file_id, &file)) return IMGTOOLERR_UNEXPECTED;
442 		free_blocks += file.blocks;
443 	}
444 	if (cfs_calc_free_space(cfs, free_blocks) < sourcef.size()) return IMGTOOLERR_NOSPACE;
445 	// delete file
446 	if (file_id != INVALID_FILE_ID)
447 	{
448 		if (!cfs_file_delete(cfs, file_id)) return IMGTOOLERR_UNEXPECTED;
449 	}
450 	// create/write destination file
451 	bytes_left = sourcef.size();
452 	i = 0;
453 	while (i < cfs->block_count_file)
454 	{
455 		if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR;
456 		if (!BLOCK_USED(buffer))
457 		{
458 			if (part_id == 0) file_id = i;
459 			memset( buffer, 0xFF, cfs->page_size - 0x02);
460 			buffer[0] = 0x80;
461 			buffer[1] = (cfs->page_size - 2) - 6 - ((part_id == 0) ? FILE_HEADER_SIZE : 0);
462 			if (bytes_left < buffer[1]) buffer[1] = bytes_left;
463 			buffer_write_16_be( buffer + 2, file_id);
464 			buffer_write_16_be( buffer + 4, part_id);
465 			if (part_id == 0)
466 			{
467 				buffer[6] = 0x20;
468 				strcpy(BLOCK_FILENAME(buffer), filename);
469 				buffer_write_32_be( buffer + 6 + FILE_HEADER_SIZE - 4, cybiko_time_setup(imgtool::datetime::now(imgtool::datetime::datetime_type::LOCAL)));
470 				sourcef.read(buffer + 6 + FILE_HEADER_SIZE, buffer[1]);
471 			}
472 			else
473 			{
474 				sourcef.read(buffer + 6, buffer[1]);
475 			}
476 			if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_WRITEERROR;
477 			bytes_left -= buffer[1];
478 			if (bytes_left == 0) break;
479 			part_id++;
480 		}
481 		i++;
482 	}
483 	// ok
484 	return IMGTOOLERR_SUCCESS;
485 }
486 
cybiko_image_delete_file(imgtool::partition & partition,const char * filename)487 static imgtoolerr_t cybiko_image_delete_file(imgtool::partition &partition, const char *filename)
488 {
489 	imgtool::image &image(partition.image());
490 	cybiko_file_system *cfs = get_cfs(image);
491 	uint16_t file_id;
492 	// check filename
493 	if (strlen(filename) > 58) return IMGTOOLERR_BADFILENAME;
494 	// find file
495 	if (!cfs_file_find(cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND;
496 	// delete file
497 	if (!cfs_file_delete(cfs, file_id)) return IMGTOOLERR_UNEXPECTED;
498 	// ok
499 	return IMGTOOLERR_SUCCESS;
500 }
501 
cybikoxt_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)502 void cybikoxt_get_info( const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
503 {
504 	switch (state)
505 	{
506 		// --- the following bits of info are returned as 64-bit signed integers ---
507 		case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES          : info->i = sizeof( cybiko_file_system); break;
508 		case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES      : info->i = sizeof( cybiko_iter); break;
509 //      case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME     : info->i = 1; break;
510 		case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME : info->i = 1; break;
511 //      case IMGTOOLINFO_INT_BLOCK_SIZE                 : info->i = 264; break;
512 		// --- the following bits of info are returned as pointers to data or functions ---
513 		case IMGTOOLINFO_PTR_OPEN        : info->open        = cybiko_image_open; break;
514 		case IMGTOOLINFO_PTR_CREATE      : info->create      = cybiko_image_create; break;
515 		case IMGTOOLINFO_PTR_CLOSE       : info->close       = cybiko_image_close; break;
516 		case IMGTOOLINFO_PTR_BEGIN_ENUM  : info->begin_enum  = cybiko_image_begin_enum; break;
517 		case IMGTOOLINFO_PTR_NEXT_ENUM   : info->next_enum   = cybiko_image_next_enum; break;
518 		case IMGTOOLINFO_PTR_FREE_SPACE  : info->free_space  = cybiko_image_free_space; break;
519 		case IMGTOOLINFO_PTR_READ_FILE   : info->read_file   = cybiko_image_read_file; break;
520 		case IMGTOOLINFO_PTR_WRITE_FILE  : info->write_file  = cybiko_image_write_file; break;
521 		case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = cybiko_image_delete_file; break;
522 		// --- the following bits of info are returned as NULL-terminated strings ---
523 		case IMGTOOLINFO_STR_NAME            : strcpy( info->s = imgtool_temp_str(), "cybikoxt"); break;
524 		case IMGTOOLINFO_STR_DESCRIPTION     : strcpy( info->s = imgtool_temp_str(), "Cybiko Xtreme File System"); break;
525 		case IMGTOOLINFO_STR_FILE            : strcpy( info->s = imgtool_temp_str(), __FILE__); break;
526 		case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "bin,nv"); break;
527 		case IMGTOOLINFO_STR_EOLN            : strcpy( info->s = imgtool_temp_str(), "\r\n"); break;
528 	}
529 }
530