1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 #include "ipf_dsk.h"
4 
5 #include <cassert>
6 
7 
8 const floppy_format_type FLOPPY_IPF_FORMAT = &floppy_image_format_creator<ipf_format>;
9 
ipf_format()10 ipf_format::ipf_format() :
11 	tinfos(),
12 	tcount(0),
13 	type(0),
14 	release(0),
15 	revision(0),
16 	encoder_type(0),
17 	encoder_revision(0),
18 	origin(0),
19 	min_cylinder(0),
20 	max_cylinder(0),
21 	min_head(0),
22 	max_head(0),
23 	credit_day(0),
24 	credit_time(0)
25 {
26 }
27 
name() const28 const char *ipf_format::name() const
29 {
30 	return "ipf";
31 }
32 
description() const33 const char *ipf_format::description() const
34 {
35 	return "SPS floppy disk image";
36 }
37 
extensions() const38 const char *ipf_format::extensions() const
39 {
40 	return "ipf";
41 }
42 
supports_save() const43 bool ipf_format::supports_save() const
44 {
45 	return false;
46 }
47 
identify(io_generic * io,uint32_t form_factor)48 int ipf_format::identify(io_generic *io, uint32_t form_factor)
49 {
50 	static const uint8_t refh[12] = { 0x43, 0x41, 0x50, 0x53, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0xd5, 0x73, 0xba };
51 	uint8_t h[12];
52 	io_generic_read(io, h, 0, 12);
53 
54 	if(!memcmp(h, refh, 12))
55 		return 100;
56 
57 	return 0;
58 }
59 
load(io_generic * io,uint32_t form_factor,floppy_image * image)60 bool ipf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
61 {
62 	uint64_t size = io_generic_size(io);
63 	std::vector<uint8_t> data(size);
64 	io_generic_read(io, &data[0], 0, size);
65 	bool res = parse(data, image);
66 	return res;
67 }
68 
r32(const uint8_t * p)69 uint32_t ipf_format::r32(const uint8_t *p)
70 {
71 	return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
72 }
73 
74 
rb(const uint8_t * & p,int count)75 uint32_t ipf_format::rb(const uint8_t *&p, int count)
76 {
77 	uint32_t v = 0;
78 	for(int i=0; i<count; i++)
79 		v = (v << 8) | *p++;
80 	return v;
81 }
82 
crc32r(const uint8_t * data,uint32_t size)83 uint32_t ipf_format::crc32r(const uint8_t *data, uint32_t size)
84 {
85 	// Reversed crc32
86 	uint32_t crc = 0xffffffff;
87 	for(uint32_t i=0; i != size; i++) {
88 		crc = crc ^ data[i];
89 		for(int j=0; j<8; j++)
90 			if(crc & 1)
91 				crc = (crc >> 1) ^ 0xedb88320;
92 			else
93 				crc = crc >> 1;
94 	}
95 	return ~crc;
96 }
97 
parse(std::vector<uint8_t> & data,floppy_image * image)98 bool ipf_format::parse(std::vector<uint8_t> &data, floppy_image *image)
99 {
100 	image->set_variant(floppy_image::DSDD); // Not handling anything else yet
101 	tcount = 84*2+1; // Usual max
102 	tinfos.resize(tcount);
103 	bool res = scan_all_tags(data);
104 	if(res)
105 		res = generate_tracks(image);
106 	tinfos.clear();
107 	return res;
108 }
109 
parse_info(const uint8_t * info)110 bool ipf_format::parse_info(const uint8_t *info)
111 {
112 	type = r32(info+12);
113 	if(type != 1)
114 		return false;
115 	encoder_type = r32(info+16); // 1 for CAPS, 2 for SPS
116 	encoder_revision = r32(info+20); // 1 always
117 	release = r32(info+24);
118 	revision = r32(info+28);
119 	origin = r32(info+32); // Original source reference
120 	min_cylinder = r32(info+36);
121 	max_cylinder = r32(info+40);
122 	min_head = r32(info+44);
123 	max_head = r32(info+48);
124 	credit_day = r32(info+52);  // year*1e4 + month*1e2 + day
125 	credit_time = r32(info+56); // hour*1e7 + min*1e5 + sec*1e3 + msec
126 	for(int i=0; i<4; i++)
127 		platform[i] = r32(info+60+4*i);
128 	for(int i=0; i<5; i++)
129 		extra[i] = r32(info+76+4*i);
130 	return true;
131 }
132 
get_index(uint32_t idx)133 ipf_format::track_info *ipf_format::get_index(uint32_t idx)
134 {
135 	if(idx > 1000)
136 		return nullptr;
137 	if(idx >= tcount) {
138 		tinfos.resize(idx+1);
139 		tcount = idx+1;
140 	}
141 
142 	return &tinfos[idx];
143 }
144 
parse_imge(const uint8_t * imge)145 bool ipf_format::parse_imge(const uint8_t *imge)
146 {
147 	track_info *t = get_index(r32(imge+64));
148 	if(!t)
149 		return false;
150 
151 	t->info_set = true;
152 
153 	t->cylinder = r32(imge+12);
154 	if(t->cylinder < min_cylinder || t->cylinder > max_cylinder)
155 		return false;
156 
157 	t->head = r32(imge+16);
158 	if(t->head < min_head || t->head > max_head)
159 		return false;
160 
161 	t->type = r32(imge+20);
162 	t->sigtype = r32(imge+24); // 1 for 2us cells, no other value valid
163 	t->size_bytes = r32(imge+28);
164 	t->index_bytes = r32(imge+32);
165 	t->index_cells = r32(imge+36);
166 	t->datasize_cells = r32(imge+40);
167 	t->gapsize_cells = r32(imge+44);
168 	t->size_cells = r32(imge+48);
169 	t->block_count = r32(imge+52);
170 	t->process = r32(imge+56); // encoder process, always 0
171 	t->weak_bits = r32(imge+60);
172 	t->reserved[0] = r32(imge+68);
173 	t->reserved[1] = r32(imge+72);
174 	t->reserved[2] = r32(imge+76);
175 
176 	return true;
177 }
178 
parse_data(const uint8_t * data,uint32_t & pos,uint32_t max_extra_size)179 bool ipf_format::parse_data(const uint8_t *data, uint32_t &pos, uint32_t max_extra_size)
180 {
181 	track_info *t = get_index(r32(data+24));
182 	if(!t)
183 		return false;
184 
185 	t->data_size_bits = r32(data+16);
186 	t->data = data+28;
187 	t->data_size = r32(data+12);
188 	if(t->data_size > max_extra_size)
189 		return false;
190 	if(crc32r(t->data, t->data_size) != r32(data+20))
191 		return false;
192 	pos += t->data_size;
193 	return true;
194 }
195 
scan_one_tag(std::vector<uint8_t> & data,uint32_t & pos,uint8_t * & tag,uint32_t & tsize)196 bool ipf_format::scan_one_tag(std::vector<uint8_t> &data, uint32_t &pos, uint8_t *&tag, uint32_t &tsize)
197 {
198 	if(data.size()-pos < 12)
199 		return false;
200 	tag = &data[pos];
201 	tsize = r32(tag+4);
202 	if(data.size()-pos < tsize)
203 		return false;
204 	uint32_t crc = r32(tag+8);
205 	tag[8] = tag[9] = tag[10] = tag[11] = 0;
206 	if(crc32r(tag, tsize) != crc)
207 		return false;
208 	pos += tsize;
209 	return true;
210 }
211 
scan_all_tags(std::vector<uint8_t> & data)212 bool ipf_format::scan_all_tags(std::vector<uint8_t> &data)
213 {
214 	uint32_t pos = 0;
215 	uint32_t size = data.size();
216 	while(pos != size) {
217 		uint8_t *tag;
218 		uint32_t tsize;
219 
220 		if(!scan_one_tag(data, pos, tag, tsize))
221 			return false;
222 
223 		switch(r32(tag)) {
224 		case 0x43415053: // CAPS
225 			if(tsize != 12)
226 				return false;
227 			break;
228 
229 		case 0x494e464f: // INFO
230 			if(tsize != 96)
231 				return false;
232 			if(!parse_info(tag))
233 				return false;
234 			break;
235 
236 		case 0x494d4745: // IMGE
237 			if(tsize != 80)
238 				return false;
239 			if(!parse_imge(tag))
240 				return false;
241 			break;
242 
243 		case 0x44415441: // DATA
244 			if(tsize != 28)
245 				return false;
246 			if(!parse_data(tag, pos, size-pos))
247 				return false;
248 			break;
249 
250 		default:
251 			return false;
252 		}
253 	}
254 	return true;
255 }
256 
generate_tracks(floppy_image * image)257 bool ipf_format::generate_tracks(floppy_image *image)
258 {
259 	for(uint32_t i = 0; i != tcount; i++) {
260 		track_info *t = &tinfos[i];
261 		if(t->info_set && t->data) {
262 			if(!generate_track(t, image))
263 				return false;
264 
265 		} else if(t->info_set || t->data)
266 			return false;
267 	}
268 	return true;
269 }
270 
rotate(std::vector<uint32_t> & track,uint32_t offset,uint32_t size)271 void ipf_format::rotate(std::vector<uint32_t> &track, uint32_t offset, uint32_t size)
272 {
273 	uint32_t done = 0;
274 	for(uint32_t bpos=0; done < size; bpos++) {
275 		uint32_t pos = bpos;
276 		uint32_t hold = track[pos];
277 		for(;;) {
278 			uint32_t npos = pos+offset;
279 			if(npos >= size)
280 				npos -= size;
281 			if(npos == bpos)
282 				break;
283 			track[pos] = track[npos];
284 			pos = npos;
285 			done++;
286 		}
287 		track[pos] = hold;
288 		done++;
289 	}
290 }
291 
mark_track_splice(std::vector<uint32_t> & track,uint32_t offset,uint32_t size)292 void ipf_format::mark_track_splice(std::vector<uint32_t> &track, uint32_t offset, uint32_t size)
293 {
294 	for(int i=0; i<3; i++) {
295 		uint32_t pos = (offset + i) % size;
296 		uint32_t v = track[pos];
297 		if((v & floppy_image::MG_MASK) == MG_0)
298 			v = (v & floppy_image::TIME_MASK) | MG_1;
299 		else if((v & floppy_image::MG_MASK) == MG_1)
300 			v = (v & floppy_image::TIME_MASK) | MG_0;
301 		track[pos] = v;
302 	}
303 }
304 
timing_set(std::vector<uint32_t> & track,uint32_t start,uint32_t end,uint32_t time)305 void ipf_format::timing_set(std::vector<uint32_t> &track, uint32_t start, uint32_t end, uint32_t time)
306 {
307 	for(uint32_t i=start; i != end; i++)
308 		track[i] = (track[i] & floppy_image::MG_MASK) | time;
309 }
310 
generate_timings(track_info * t,std::vector<uint32_t> & track,const std::vector<uint32_t> & data_pos,const std::vector<uint32_t> & gap_pos)311 bool ipf_format::generate_timings(track_info *t, std::vector<uint32_t> &track, const std::vector<uint32_t> &data_pos, const std::vector<uint32_t> &gap_pos)
312 {
313 	timing_set(track, 0, t->size_cells, 2000);
314 
315 	switch(t->type) {
316 	case 2: break;
317 
318 	case 3:
319 		if(t->block_count >= 4)
320 			timing_set(track, gap_pos[3], data_pos[4], 1890);
321 		if(t->block_count >= 5) {
322 			timing_set(track, data_pos[4], gap_pos[4], 1890);
323 			timing_set(track, gap_pos[4], data_pos[5], 1990);
324 		}
325 		if(t->block_count >= 6) {
326 			timing_set(track, data_pos[5], gap_pos[5], 1990);
327 			timing_set(track, gap_pos[5], data_pos[6], 2090);
328 		}
329 		if(t->block_count >= 7)
330 			timing_set(track, data_pos[6], gap_pos[6], 2090);
331 		break;
332 
333 	case 4:
334 		timing_set(track, gap_pos[t->block_count-1], data_pos[0], 1890);
335 		timing_set(track, data_pos[0], gap_pos[0], 1890);
336 		timing_set(track, gap_pos[0], data_pos[1], 1990);
337 		if(t->block_count >= 2) {
338 			timing_set(track, data_pos[1], gap_pos[1], 1990);
339 			timing_set(track, gap_pos[1], data_pos[2], 2090);
340 		}
341 		if(t->block_count >= 3)
342 			timing_set(track, data_pos[2], gap_pos[2], 2090);
343 		break;
344 
345 	case 5:
346 		if(t->block_count >= 6)
347 			timing_set(track, data_pos[5], gap_pos[5], 2100);
348 		break;
349 
350 	case 6:
351 		if(t->block_count >= 2)
352 			timing_set(track, data_pos[1], gap_pos[1], 2200);
353 		if(t->block_count >= 3)
354 			timing_set(track, data_pos[2], gap_pos[2], 1800);
355 		break;
356 
357 	case 7:
358 		if(t->block_count >= 2)
359 			timing_set(track, data_pos[1], gap_pos[1], 2100);
360 		break;
361 
362 	case 8:
363 		if(t->block_count >= 2)
364 			timing_set(track, data_pos[1], gap_pos[1], 2200);
365 		if(t->block_count >= 3)
366 			timing_set(track, data_pos[2], gap_pos[2], 2100);
367 		if(t->block_count >= 5)
368 			timing_set(track, data_pos[4], gap_pos[4], 1900);
369 		if(t->block_count >= 6)
370 			timing_set(track, data_pos[5], gap_pos[5], 1800);
371 		if(t->block_count >= 7)
372 			timing_set(track, data_pos[6], gap_pos[6], 1700);
373 		break;
374 
375 	case 9: {
376 		uint32_t mask = r32(t->data + 32*t->block_count + 12);
377 		for(uint32_t i=1; i<t->block_count; i++)
378 			timing_set(track, data_pos[i], gap_pos[i], mask & (1 << (i-1)) ? 1900 : 2100);
379 		break;
380 	}
381 
382 	default:
383 		return false;
384 	}
385 
386 	return true;
387 }
388 
generate_track(track_info * t,floppy_image * image)389 bool ipf_format::generate_track(track_info *t, floppy_image *image)
390 {
391 	if(!t->size_cells)
392 		return true;
393 
394 	if(t->data_size < 32*t->block_count)
395 		return false;
396 
397 	// Annoyingly enough, too small gaps are ignored, changing the
398 	// total track size.  Artifact stemming from the byte-only support
399 	// of old times?
400 	t->size_cells = block_compute_real_size(t);
401 
402 	if(t->index_cells >= t->size_cells)
403 		return false;
404 
405 	std::vector<uint32_t> track(t->size_cells);
406 	std::vector<uint32_t> data_pos(t->block_count+1);
407 	std::vector<uint32_t> gap_pos(t->block_count);
408 	std::vector<uint32_t> splice_pos(t->block_count);
409 
410 	bool context = false;
411 	uint32_t pos = 0;
412 	for(uint32_t i = 0; i != t->block_count; i++) {
413 		if(!generate_block(t, i, i == t->block_count-1 ? t->size_cells - t->index_cells : 0xffffffff, track, pos, data_pos[i], gap_pos[i], splice_pos[i], context)) {
414 			return false;
415 		}
416 	}
417 	if(pos != t->size_cells) {
418 		return false;
419 	}
420 
421 	data_pos[t->block_count] = pos;
422 
423 	mark_track_splice(track, splice_pos[t->block_count-1], t->size_cells);
424 
425 	if(!generate_timings(t, track, data_pos, gap_pos)) {
426 		return false;
427 	}
428 
429 	if(t->index_cells)
430 		rotate(track, t->size_cells - t->index_cells, t->size_cells);
431 
432 	generate_track_from_levels(t->cylinder, t->head, track, splice_pos[t->block_count-1] + t->index_cells, image);
433 
434 	return true;
435 }
436 
track_write_raw(std::vector<uint32_t>::iterator & tpos,const uint8_t * data,uint32_t cells,bool & context)437 void ipf_format::track_write_raw(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t cells, bool &context)
438 {
439 	for(uint32_t i=0; i != cells; i++)
440 		*tpos++ = data[i>>3] & (0x80 >> (i & 7)) ? MG_1 : MG_0;
441 	if(cells)
442 		context = tpos[-1] == MG_1;
443 }
444 
track_write_mfm(std::vector<uint32_t>::iterator & tpos,const uint8_t * data,uint32_t start_offset,uint32_t patlen,uint32_t cells,bool & context)445 void ipf_format::track_write_mfm(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t start_offset, uint32_t patlen, uint32_t cells, bool &context)
446 {
447 	patlen *= 2;
448 	for(uint32_t i=0; i != cells; i++) {
449 		uint32_t pos = (i + start_offset) % patlen;
450 		bool bit = data[pos>>4] & (0x80 >> ((pos >> 1) & 7));
451 		if(pos & 1) {
452 			*tpos++ = bit ? MG_1 : MG_0;
453 			context = bit;
454 		} else
455 			*tpos++ = context || bit ? MG_0 : MG_1;
456 	}
457 }
458 
track_write_weak(std::vector<uint32_t>::iterator & tpos,uint32_t cells)459 void ipf_format::track_write_weak(std::vector<uint32_t>::iterator &tpos, uint32_t cells)
460 {
461 	for(uint32_t i=0; i != cells; i++)
462 		*tpos++ = floppy_image::MG_N;
463 }
464 
generate_block_data(const uint8_t * data,const uint8_t * dlimit,std::vector<uint32_t>::iterator tpos,std::vector<uint32_t>::iterator tlimit,bool & context)465 bool ipf_format::generate_block_data(const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, std::vector<uint32_t>::iterator tlimit, bool &context)
466 {
467 	for(;;) {
468 		if(data >= dlimit)
469 			return false;
470 		uint8_t val = *data++;
471 		if((val >> 5) > dlimit-data)
472 			return false;
473 		uint32_t param = rb(data, val >> 5);
474 		uint32_t tleft = tlimit - tpos;
475 		switch(val & 0x1f) {
476 		case 0: // End of description
477 			return !tleft;
478 
479 		case 1: // Raw bytes
480 			if(8*param > tleft)
481 				return false;
482 			track_write_raw(tpos, data, 8*param, context);
483 			data += param;
484 			break;
485 
486 		case 2: // MFM-decoded data bytes
487 		case 3: // MFM-decoded gap bytes
488 			if(16*param > tleft)
489 				return false;
490 			track_write_mfm(tpos, data, 0, 8*param, 16*param, context);
491 			data += param;
492 			break;
493 
494 		case 5: // Weak bytes
495 			if(16*param > tleft)
496 				return false;
497 			track_write_weak(tpos, 16*param);
498 			context = 0;
499 			break;
500 
501 		default:
502 			return false;
503 		}
504 	}
505 }
506 
generate_block_gap_0(uint32_t gap_cells,uint8_t pattern,uint32_t & spos,uint32_t ipos,std::vector<uint32_t>::iterator & tpos,bool & context)507 bool ipf_format::generate_block_gap_0(uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, std::vector<uint32_t>::iterator &tpos, bool &context)
508 {
509 	spos = ipos >= 16 && ipos+16 <= gap_cells ? ipos : gap_cells >> 1;
510 	track_write_mfm(tpos, &pattern, 0, 8, spos, context);
511 	uint32_t delta = 0;
512 	if(gap_cells & 1) {
513 		*tpos++ = MG_0;
514 		delta++;
515 	}
516 	track_write_mfm(tpos, &pattern, spos+delta-gap_cells, 8, gap_cells-spos-delta, context);
517 	return true;
518 }
519 
gap_description_to_reserved_size(const uint8_t * & data,const uint8_t * dlimit,uint32_t & res_size)520 bool ipf_format::gap_description_to_reserved_size(const uint8_t *&data, const uint8_t *dlimit, uint32_t &res_size)
521 {
522 	res_size = 0;
523 	for(;;) {
524 		if(data >= dlimit)
525 			return false;
526 		uint8_t val = *data++;
527 		if((val >> 5) > dlimit-data)
528 			return false;
529 		uint32_t param = rb(data, val >> 5);
530 		switch(val & 0x1f) {
531 		case 0:
532 			return true;
533 		case 1:
534 			res_size += param*2;
535 			break;
536 		case 2:
537 			data += (param+7)/8;
538 			break;
539 		default:
540 			return false;
541 		}
542 	}
543 }
544 
generate_gap_from_description(const uint8_t * & data,const uint8_t * dlimit,std::vector<uint32_t>::iterator tpos,uint32_t size,bool pre,bool & context)545 bool ipf_format::generate_gap_from_description(const uint8_t *&data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, uint32_t size, bool pre, bool &context)
546 {
547 	const uint8_t *data1 = data;
548 	uint32_t res_size;
549 	if(!gap_description_to_reserved_size(data1, dlimit, res_size))
550 		return false;
551 
552 	if(res_size > size)
553 		return false;
554 	uint8_t pattern[16];
555 	memset(pattern, 0, sizeof(pattern));
556 	uint32_t pattern_size = 0;
557 
558 	uint32_t pos = 0, block_size = 0;
559 	for(;;) {
560 		uint8_t val = *data++;
561 		uint32_t param = rb(data, val >> 5);
562 		switch(val & 0x1f) {
563 		case 0:
564 			return size == pos;
565 
566 		case 1:
567 			if(block_size)
568 				return false;
569 			block_size = param*2;
570 			pattern_size = 0;
571 			break;
572 
573 		case 2:
574 			// You can't have a pattern at the start of a pre-slice
575 			// gap if there's a size afterwards
576 			if(pre && res_size && !block_size)
577 				return false;
578 			// You can't have two consecutive patterns
579 			if(pattern_size)
580 				return false;
581 			pattern_size = param;
582 			if(pattern_size > sizeof(pattern)*8)
583 				return false;
584 
585 			memcpy(pattern, data, (pattern_size+7)/8);
586 			data += (pattern_size+7)/8;
587 			if(pre) {
588 				if(!block_size)
589 					block_size = size;
590 				else if(pos + block_size == res_size)
591 					block_size = size - pos;
592 				if(pos + block_size > size)
593 					return false;
594 				//              printf("pat=%02x size=%d pre\n", pattern[0], block_size);
595 				track_write_mfm(tpos, pattern, 0, pattern_size, block_size, context);
596 				pos += block_size;
597 			} else {
598 				if(pos == 0 && block_size && res_size != size)
599 					block_size = size - (res_size-block_size);
600 				if(!block_size)
601 					block_size = size - res_size;
602 				if(pos + block_size > size)
603 					return false;
604 				//              printf("pat=%02x block_size=%d size=%d res_size=%d post\n", pattern[0], block_size, size, res_size);
605 				track_write_mfm(tpos, pattern, -block_size, pattern_size, block_size, context);
606 				pos += block_size;
607 			}
608 			block_size = 0;
609 			break;
610 		}
611 	}
612 }
613 
614 
generate_block_gap_1(uint32_t gap_cells,uint32_t & spos,uint32_t ipos,const uint8_t * data,const uint8_t * dlimit,std::vector<uint32_t>::iterator & tpos,bool & context)615 bool ipf_format::generate_block_gap_1(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context)
616 {
617 	if(ipos >= 16 && ipos < gap_cells-16)
618 		spos = ipos;
619 	else
620 		spos = 0;
621 	return generate_gap_from_description(data, dlimit, tpos, gap_cells, true, context);
622 }
623 
generate_block_gap_2(uint32_t gap_cells,uint32_t & spos,uint32_t ipos,const uint8_t * data,const uint8_t * dlimit,std::vector<uint32_t>::iterator & tpos,bool & context)624 bool ipf_format::generate_block_gap_2(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context)
625 {
626 	if(ipos >= 16 && ipos < gap_cells-16)
627 		spos = ipos;
628 	else
629 		spos = gap_cells;
630 	return generate_gap_from_description(data, dlimit, tpos, gap_cells, false, context);
631 }
632 
generate_block_gap_3(uint32_t gap_cells,uint32_t & spos,uint32_t ipos,const uint8_t * data,const uint8_t * dlimit,std::vector<uint32_t>::iterator & tpos,bool & context)633 bool ipf_format::generate_block_gap_3(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos,  bool &context)
634 {
635 	if(ipos >= 16 && ipos < gap_cells-16)
636 		spos = ipos;
637 	else {
638 		uint32_t presize, postsize;
639 		const uint8_t *data1 = data;
640 		if(!gap_description_to_reserved_size(data1, dlimit, presize))
641 			return false;
642 		if(!gap_description_to_reserved_size(data1, dlimit, postsize))
643 			return false;
644 		if(presize+postsize > gap_cells)
645 			return false;
646 
647 		spos = presize + (gap_cells - presize - postsize)/2;
648 	}
649 	if(!generate_gap_from_description(data, dlimit, tpos, spos, true, context))
650 		return false;
651 	uint32_t delta = 0;
652 	if(gap_cells & 1) {
653 		tpos[spos] = MG_0;
654 		delta++;
655 	}
656 
657 	return generate_gap_from_description(data, dlimit, tpos+spos+delta, gap_cells - spos - delta, false, context);
658 }
659 
generate_block_gap(uint32_t gap_type,uint32_t gap_cells,uint8_t pattern,uint32_t & spos,uint32_t ipos,const uint8_t * data,const uint8_t * dlimit,std::vector<uint32_t>::iterator tpos,bool & context)660 bool ipf_format::generate_block_gap(uint32_t gap_type, uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, bool &context)
661 {
662 	switch(gap_type) {
663 	case 0:
664 		return generate_block_gap_0(gap_cells, pattern, spos, ipos, tpos, context);
665 	case 1:
666 		return generate_block_gap_1(gap_cells, spos, ipos, data, dlimit, tpos, context);
667 	case 2:
668 		return generate_block_gap_2(gap_cells, spos, ipos, data, dlimit, tpos, context);
669 	case 3:
670 		return generate_block_gap_3(gap_cells, spos, ipos, data, dlimit, tpos, context);
671 	default:
672 		return false;
673 	}
674 }
675 
generate_block(track_info * t,uint32_t idx,uint32_t ipos,std::vector<uint32_t> & track,uint32_t & pos,uint32_t & dpos,uint32_t & gpos,uint32_t & spos,bool & context)676 bool ipf_format::generate_block(track_info *t, uint32_t idx, uint32_t ipos, std::vector<uint32_t> &track, uint32_t &pos, uint32_t &dpos, uint32_t &gpos, uint32_t &spos, bool &context)
677 {
678 	const uint8_t *data = t->data;
679 	const uint8_t *data_end = t->data + t->data_size;
680 	const uint8_t *thead = data + 32*idx;
681 	uint32_t data_cells = r32(thead);
682 	uint32_t gap_cells = r32(thead+4);
683 
684 	if(gap_cells < 8)
685 		gap_cells = 0;
686 
687 	// +8  = gap description offset / datasize in bytes (when gap type = 0)
688 	// +12 =                      1 / gap size in bytes (when gap type = 0)
689 	// +16 = 1
690 	// +20 = gap type
691 	// +24 = type 0 gap pattern (8 bits) / speed mask for sector 0 track type 9
692 	// +28 = data description offset
693 
694 	dpos = pos;
695 	gpos = dpos + data_cells;
696 	pos = gpos + gap_cells;
697 	if(pos > t->size_cells)
698 		return false;
699 	if(!generate_block_data(data + r32(thead+28), data_end, track.begin()+dpos, track.begin()+gpos, context))
700 		return false;
701 	if(!generate_block_gap(r32(thead+20), gap_cells, r32(thead+24), spos, ipos > gpos ? ipos-gpos : 0, data + r32(thead+8), data_end, track.begin()+gpos, context))
702 		return false;
703 	spos += gpos;
704 
705 	return true;
706 }
707 
block_compute_real_size(track_info * t)708 uint32_t ipf_format::block_compute_real_size(track_info *t)
709 {
710 	uint32_t size = 0;
711 	const uint8_t *thead = t->data;
712 	for(unsigned int i=0; i != t->block_count; i++) {
713 		uint32_t data_cells = r32(thead);
714 		uint32_t gap_cells = r32(thead+4);
715 		if(gap_cells < 8)
716 			gap_cells = 0;
717 
718 		size += data_cells + gap_cells;
719 		thead += 32;
720 	}
721 	return size;
722 }
723