// license:BSD-3-Clause
// copyright-holders:Michael Zapf

/*
    The hfe_format class implements the HFE format that is used for the Lotharek
    floppy emulator.

    Format definition according to the official document:

    File header (0x0000 - 0x01FF, 512 bytes)
    ----------------------------------------

    typedef struct picfileformatheader_
    {
       uint8_t HEADERSIGNATURE[8];        // 0: "HXCPICFE"
       uint8_t formatrevision;            // 8: Revision 0
       uint8_t number_of_track;           // 9: Number of track in the file
       uint8_t number_of_side;            // 10: Number of valid side (Not used by the emulator)
       uint8_t track_encoding;            // 11: Track Encoding mode
                                        // (Used for the write support - Please see the list above)
       uint16_t bitRate;                  // 12: Bitrate in Kbit/s. Ex : 250=250000bits/s
                                        // Max value : 500
       uint16_t floppyRPM;                // 14: Rotation per minute (Not used by the emulator)
       uint8_t floppyinterfacemode;       // 16: Floppy interface mode. (Please see the list above.)
       uint8_t dnu;                       // 17: Free
       uint16_t track_list_offset;        // 18: Offset of the track list LUT in block of 512 bytes
                                        // (Ex: 1=0x200)
       uint8_t write_allowed;             // 20: The Floppy image is write protected ?
       uint8_t single_step;               // 21: 0xFF : Single Step – 0x00 Double Step mode
       uint8_t track0s0_altencoding;      // 22: 0x00 : Use an alternate track_encoding for track 0 Side 0
       uint8_t track0s0_encoding;         // 23: alternate track_encoding for track 0 Side 0
       uint8_t track0s1_altencoding;      // 24: 0x00 : Use an alternate track_encoding for track 0 Side 1
       uint8_t track0s1_encoding;         // 25: alternate track_encoding for track 0 Side 1
    } picfileformatheader;

    Byte order for uint16_t is little endian.

    floppyintefacemode values are defined in the header file as floppymode_t,
    track_encodings are defined as encoding_t

    track0s0_encoding is only valid when track0s0_altencoding==0xff
    track0s1_encoding is only valid when track0s1_altencoding==0xff

    Track offset lookup table (at 0x0200)
    -------------------------------------

    typedef struct pictrack_
    {
        uint16_t offset;     // Offset of the track data in blocks of 512 bytes (Ex: 2=0x400)
        uint16_t track_len;  // Length of the track data in byte.
    } pictrack;

    This table has a size of  number_of_track*4 bytes.

    Track data
    ----------

    (first possible occurance at 0x0400)

    Each track is encoded in a sequence of cell levels which are represented
    by bits in the data.

     +--------+--------+--------+--------+---- ........ ---+--------+--------+
     | Head 0 | Head 1 | Head 0 | Head 1 | Hea ........  1 | Head 0 | Head 1 |
     +--------+--------+--------+--------+---- ........ ---+--------+--------+
     |     Block 0     |     Block 1     |                 |    Block n-1    |

    Each block (Head 0 + Head 1) is 0x200 bytes long, with 0x100 bytes for
    each head. Block n-1 may be partially filled, e.g. with 64 bytes for
    head 0 and 64 bytes for head 1. The contents for head 1 in block n-1
    start at offxet 0x100 nevertheless:

     +--------+--------+
     |]]]]  0 |]]]]  1 |
     +--------+--------+
     |    Block n-1    |


    Each byte in the track data is a sequence of cell sample levels
    according to the sample rate. Bit order is little endian:

     Bits
     7 6 5 4 3 2 1 0   15 14 13 12 11 10 9 8   23 22 21 20 19 18 17 16

     0-bits indicate no change, 1-bits indicate flux level change.

    To encode the byte 0x4e in MFM at 250 kbit/s, the following data bytes
    are used:

     Byte:          0  1  0  0  1  1  1  0
     MDM encoding: 10 01 00 10 01 01 01 00
     Reversed order: 0010 1010 0100 1001     = 2a 49

    Interestingly, FM-encoded disks are usually sampled at 250 kbit/s,
    like MFM, although FM only delivers 125 kbit/s. This oversampling leads
    to zero bits (no change) every two positions. See below for details.

     TODO:
     - Handle double-stepping for medium.tracks=40, drive.tracks=80
*/

#include "hxchfe_dsk.h"

#include "ioprocs.h"

#include "osdcore.h" // osd_printf_*


#define HFE_FORMAT_HEADER   "HXCPICFE"

#define HEADER_LENGTH 512
#define TRACK_TABLE_LENGTH 1024

hfe_format::hfe_format() : floppy_image_format_t(),
   m_cylinders(0),
   m_heads(0),
   m_track_encoding(UNKNOWN_ENCODING),
   m_bit_rate(0),
   m_floppy_rpm(0),
   m_interface_mode(DISABLE_FLOPPYMODE),
   m_write_allowed(true),
   m_single_step(true),
   m_track0s0_has_altencoding(false),
   m_track0s0_encoding(UNKNOWN_ENCODING),
   m_track0s1_has_altencoding(false),
   m_track0s1_encoding(UNKNOWN_ENCODING),
   m_selected_mode(DISABLE_FLOPPYMODE),
   m_selected_encoding(UNKNOWN_ENCODING)
{
}

const char *hfe_format::name() const
{
	return "hfe";
}

const char *hfe_format::description() const
{
	return "SDCard HxC Floppy Emulator HFE File format";
}

const char *hfe_format::extensions() const
{
	return "hfe";
}

bool hfe_format::supports_save() const
{
	return true;
}

int hfe_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants)
{
	uint8_t header[8];

	size_t actual;
	io.read_at(0, &header, sizeof(header), actual);
	if ( memcmp( header, HFE_FORMAT_HEADER, 8 ) ==0) {
		return 100;
	}
	return 0;
}

bool hfe_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image *image)
{
	size_t actual;
	uint8_t header[HEADER_LENGTH];
	uint8_t track_table[TRACK_TABLE_LENGTH];

	int drivecyl, driveheads;
	image->get_maximal_geometry(drivecyl, driveheads);

	// read header
	io.read_at(0, header, HEADER_LENGTH, actual);

	// get values
	// Format revision must be 0
	if (header[8] != 0)
	{
		osd_printf_error("hxchfe: Invalid format revision. Expected 0, got %d.\n", header[8]);
		return false;
	}

	m_cylinders = header[9] & 0xff;
	m_heads = header[10] & 0xff;

	if (drivecyl < m_cylinders)
	{
		if (m_cylinders - drivecyl > DUMP_THRESHOLD)
		{
			osd_printf_error("hxchfe: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", m_cylinders, drivecyl);
			return false;
		}
		else
		{
			// Some dumps has a few excess tracks to be safe,
			// lets be nice and just skip those tracks
			osd_printf_warning("hxchfe: Floppy disk has a slight excess of tracks for this drive that will be discarded (floppy tracks=%d, drive tracks=%d).\n", m_cylinders, drivecyl);
			m_cylinders = drivecyl;
		}
	}

	if (m_cylinders <= drivecyl/2)
	{
		osd_printf_error("hxchfe: Double stepping not yet supported (floppy tracks=%d, drive tracks=%d).\n", m_cylinders, drivecyl);
		return false;
	}

	m_track_encoding = (encoding_t)(header[11] & 0xff);

	if (m_track_encoding > EMU_FM_ENCODING)
	{
		osd_printf_error("hxchfe: Unknown track encoding %d.\n", m_track_encoding);
		return false;
	}

	m_bit_rate = (header[12] & 0xff) | ((header[13] & 0xff)<<8);

	if (m_bit_rate > 500)
	{
		osd_printf_error("hxchfe: Unsupported bit rate %d.\n", m_bit_rate);
		return false;
	}
	int samplelength = 500000 / m_bit_rate;

	// Not used in the HxC emulator
	m_floppy_rpm = (header[14] & 0xff) | ((header[15] & 0xff)<<8);

	m_interface_mode = (floppymode_t)(header[16] & 0xff);
	if (m_interface_mode > S950_HD_FLOPPYMODE)
	{
		osd_printf_error("hxchfe: Unknown interface mode %d.\n", m_interface_mode);
		return false;
	}

	m_write_allowed = (header[20] != 0);
	m_single_step = (header[21] != 0);
	m_track0s0_has_altencoding = (header[22] == 0x00);
	m_track0s0_encoding = (encoding_t)(header[23] & 0xff);
	m_track0s1_has_altencoding = (header[24] == 0x00);
	m_track0s1_encoding = (encoding_t)(header[25] & 0xff);

	// read track lookup table (multiple of 512)
	int table_offset = (header[18] & 0xff) | ((header[19] & 0xff)<<8);

	io.read_at(table_offset<<9, track_table, TRACK_TABLE_LENGTH, actual);

	for (int i=0; i < m_cylinders; i++)
	{
		m_cyl_offset[i] = (track_table[4*i] & 0xff) | ((track_table[4*i+1] & 0xff)<<8);
		m_cyl_length[i] = (track_table[4*i+2] & 0xff) | ((track_table[4*i+3] & 0xff)<<8);
	}

	// Load the tracks
	std::vector<uint8_t> cylinder_buffer;
	for(int cyl=0; cyl < m_cylinders; cyl++)
	{
		// actual data read
		// The HFE format defines an interleave of the two sides per cylinder
		// at every 256 bytes
		cylinder_buffer.resize(m_cyl_length[cyl]);
		io.read_at(m_cyl_offset[cyl]<<9, &cylinder_buffer[0], m_cyl_length[cyl], actual);

		generate_track_from_hfe_bitstream(cyl, 0, samplelength, &cylinder_buffer[0], m_cyl_length[cyl], image);
		if (m_heads == 2)
			generate_track_from_hfe_bitstream(cyl, 1, samplelength, &cylinder_buffer[0], m_cyl_length[cyl], image);
	}

	bool success = true;

	// Find variant
	if (m_track_encoding == ISOIBM_FM_ENCODING || m_track_encoding == EMU_FM_ENCODING)
		// FM is for single density
		image->set_variant((m_heads==1)? floppy_image::SSSD : floppy_image::DSSD);
	else
	{
		// MFM encoding is for everything else
		if (m_track_encoding == ISOIBM_MFM_ENCODING || m_track_encoding == AMIGA_MFM_ENCODING)
		{
			// Each cylinder contains the samples of both sides, 8 samples per
			// byte; the bitRate determines how many samples constitute a cell

			// DSDD: 360 KiB (5.25")= 2*40*18*256; 100000 cells/track, 2 us, bit rate = 250 kbit/s
			// DSDD: 720 KiB (3.5") = 2*80*18*256; 100000 cells/track, 2 us, 250 kbit/s
			// DSHD: 1.4 MiB = 2*80*18*512 bytes; 200000 cells/track, 1 us, 500 kbit/s
			// DSED: 2.8 MiB = 2*80*36*512 bytes; 400000 cells/track, 500 ns, 1 Mbit/s

			// Use cylinder 1 (cyl 0 may have special encodings)
			int cellcount = (m_cyl_length[1] * 8 / 2) * 250 / m_bit_rate;
			if (cellcount > 300000)
				image->set_variant(floppy_image::DSED);
			else
			{
				if (cellcount > 150000)
					image->set_variant(floppy_image::DSHD);
				else
				{
					if (cellcount > 90000)
						// We cannot distinguish DSDD from DSQD without knowing the size of the floppy disk
						image->set_variant((m_heads==1)? floppy_image::SSDD : floppy_image::DSDD);
				}
			}
		}
		else
			success = false;
	}
	return success;
}

void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int samplelength, const uint8_t *trackbuf, int track_end, floppy_image *image)
{
	// HFE has a minor issue: The track images do not sum up to 200 ms.
	// Tracks are samples at 250 kbit/s for both FM and MFM, which yields
	// 50000 data bits (100000 samples) for MFM, while FM is twice oversampled
	// (4 samples per actual data bit)
	// Hence, for both FM and MFM, we need 100000 samples.

	// Track length 61B0 (both sides, FM)
	// 100 + 100 + ... + 100 + (B0+50)    = 3000 + B0 + 50 (pad)
	//    100 + 100 + .... + 100  +  B0   = 3000 + B0 = 99712 samples   (-288)

	// Track length 61C0 (both sides, MFM)
	// 100 + 100 + ... + 100 + (C0+40)       = 3000 + C0 + 40 (pad)
	//    100 + 100 + .... + 100   +   C0    = 3000 + C0 = 99840 samples   (-160)

	// Solution: Repeat the last byte until we have enough samples
	// Note: We do not call normalize_times here because we're doing the job here

	// HFE does not define subtracks; set to 0

	// MG_1 / MG_0 are (logical) levels that indicate transition / no change
	// MG_A / MG_B are physical flux directions
	//
	// Cell: | AAAABBBB | = MG_1 = | BBBBAAAA |
	//       | AAAAAAAA | = MG_0 = | BBBBBBBB |

	std::vector<uint32_t> &dest = image->get_buffer(cyl, head, 0);
	dest.clear();

	// Start with MG_A
	uint32_t cbit = floppy_image::MG_A;

	int offset = 0x100;

	if (head==0)
	{
		offset = 0;
		track_end -= 0x0100;
	}

	uint8_t current = 0;
	int time  = 0;

	dest.push_back(cbit | time);

	cbit = floppy_image::MG_B;

	// Oversampled FM images (250 kbit/s) start with a 0, where a 1 is
	// expected for 125 kbit/s.
	// In order to make an oversampled image look like a normally sampled one,
	// we position the transition at 500 ns before the cell end.
	// The HFE format has a 1 us minimum cell size; this means that a normally
	// sampled FM image with 11111... at the begining means
	// 125 kbit/s:    1   1   1   1   1...
	// 250 kbit/s:   01  01  01  01  01...
	// 500 kbit/s: 00010001000100010001...
	//
	//   -500             3500            7500            11500
	//     +-|---:---|---:-+ |   :   |   : +-|---:---|---:-+ |
	//     | |   :   |   : | |   :   |   : | |   :   |   : | |
	//     | |   :   |   : +-|---:---|---:-+ |   :   |   : +-|
	//  -500 0      2000    4000    6000    8000   10000   12000
	//
	//  3500 (1)     samplelength - 500
	//  7500 (1)     +samplelength
	// 11500 (1)     +samplelength
	// 15500 (1)     +samplelength
	//
	//  Double samples
	//
	//  1500 (0)    samplelength - 500
	//  3500 (1)    +samplelength
	//  5500 (0)    +samplelength
	//  7500 (1)    +samplelength
	//  9500 (0)    +samplelength
	// 11500 (1)    +samplelength

	time = -500;

	// We are creating a sequence of timestamps with flux info
	// Note that the flux change occurs in the last quarter of a cell

	while (time < 200000000)   // one rotation in nanosec
	{
		current = trackbuf[offset];
		for (int j=0; j < 8; j++)
		{
			time += samplelength;
			if ((current & 1)!=0)
			{
				// Append another transition to the vector
				dest.push_back(cbit | time);

				// Toggle the cell level
				cbit = (cbit == floppy_image::MG_A)? floppy_image::MG_B : floppy_image::MG_A;
			}

			// HFE uses little-endian bit order
			current >>= 1;
		}
		offset++;
		if ((offset & 0xff)==0) offset += 0x100;

		// When we have not reached the track end (after 0.2 sec) but run
		// out of samples, repeat the last value
		if (offset >= track_end) offset = track_end - 1;
	}

	image->set_write_splice_position(cyl, head, 0, 0);
}

bool hfe_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, floppy_image *image)
{
	size_t actual;
	std::vector<uint8_t> cylbuf;

	// Create a buffer that is big enough to handle HD formats. We don't
	// know the track length until we generate the HFE bitstream.
	cylbuf.resize(0x10000);

	uint8_t header[HEADER_LENGTH];
	uint8_t track_table[TRACK_TABLE_LENGTH];

	int track_end = 0x61c0;
	int samplelength = 2000;

	// Set up header
	const char* sig = "HXCPICFE";
	memcpy(header, sig, 8);

	header[8] = 0;
	// Can we change the number of tracks or heads?
	image->get_actual_geometry(m_cylinders, m_heads);

	header[9] = m_cylinders;
	header[10] = m_heads;
	// Floppy RPM is not used
	header[14] = 0;
	header[15] = 0;

	// Bit rate and encoding will be set later, they may have changed by
	// reformatting. The selected encoding is UNKNOWN_ENCODING unless
	// explicitly set
	m_track_encoding = m_selected_encoding;

	// Take the old mode, unless we have specified a mode
	header[16] = (m_selected_mode != DISABLE_FLOPPYMODE)? m_selected_mode : m_interface_mode;
	header[17] = 0;

	// The track lookup table is located at offset 0x200 (as 512 multiple)
	header[18] = 1;
	header[19] = 0;

	header[20] = m_write_allowed? 0xff : 0x00;
	header[21] = m_single_step? 0xff : 0x00;

	// TODO: Allow for divergent track 0 format
	header[22] = m_track0s0_has_altencoding? 0x00 : 0xff;
	header[23] = m_track0s0_encoding;
	header[24] = m_track0s1_has_altencoding? 0x00 : 0xff;
	header[25] = m_track0s1_encoding;

	// Fill the remaining bytes with 0xff
	for (int i=26; i < HEADER_LENGTH; i++) header[i] = 0xff;

	// Don't write yet; we still have to find out the bit rate.

	// We won't have more than 200000 cells on the track
	for (int cyl=0; cyl < m_cylinders; cyl++)
	{
		// After the call, the encoding will be set to FM or MFM
		generate_hfe_bitstream_from_track(cyl, 0, samplelength, m_track_encoding, &cylbuf[0], track_end, image);
		if (m_heads == 2)
			generate_hfe_bitstream_from_track(cyl, 1, samplelength, m_track_encoding, &cylbuf[0], track_end, image);

		if (cyl==0)
		{
			// Complete the header and write it
			header[11] = m_track_encoding;
			m_bit_rate = 500000/samplelength;
			header[12] = m_bit_rate & 0xff;
			header[13] = (m_bit_rate >> 8) & 0xff;

			// Now write the header
			io.write_at(0, header, HEADER_LENGTH, actual);

			// Set up the track lookup table
			// We need the encoding value to be sure about the track length
			int len = (m_track_encoding==ISOIBM_FM_ENCODING)? 0x61b0 : 0x61c0;
			int pos = 0x400;

			for (int i=0; i < m_cylinders; i++)
			{
				m_cyl_offset[i] = (pos >> 9);
				m_cyl_length[i] = len;
				pos += (len + 0x1ff) & 0xfe00;
				track_table[i*4] = m_cyl_offset[i] & 0xff;
				track_table[i*4+1] = (m_cyl_offset[i]>>8) & 0xff;
				track_table[i*4+2] = len & 0xff;
				track_table[i*4+3] = (len>>8) & 0xff;
			}
			// Set the remainder to 0xff
			for (int i=m_cylinders*4; i < TRACK_TABLE_LENGTH; i++)
				track_table[i] = 0xff;

			io.write_at(0x200, track_table, TRACK_TABLE_LENGTH, actual);
		}
		// Write the current cylinder
		io.write_at(m_cyl_offset[cyl]<<9, &cylbuf[0], (m_cyl_length[cyl] + 0x1ff) & 0xfe00, actual);
	}
	return true;
}

void hfe_format::generate_hfe_bitstream_from_track(int cyl, int head, int& samplelength, encoding_t& encoding, uint8_t *cylinder_buffer, int track_end, floppy_image *image)
{
	// We are using an own implementation here because the result of the
	// parent class method would require some post-processing that we
	// can easily avoid.

	// See floppy_image_format_t::generate_bitstream_from_track
	// as the original code

	// No subtracks definded
	std::vector<uint32_t> &tbuf = image->get_buffer(cyl, head, 0);
	if (tbuf.size() <= 1)
	{
		// Unformatted track
		// TODO must handle that according to HFE
		int track_size = 200000000/samplelength;
		memset(cylinder_buffer, 0, (track_size+7)/8);
		return;
	}

	// Find out whether we have FM or MFM recording, and determine the bit rate.
	// This is needed for the format header.
	//
	// The encoding may have changed by reformatting; we cannot rely on the
	// header when loading.
	//
	// FM:   encoding 1    -> flux length = 4 us (min)          ambivalent
	//       encoding 10   -> flux length = 8 us (max)          ambivalent
	// MFM:  encoding 10   -> flux length = 4 us (min, DD)      ambivalent
	//       encoding 100  -> flux length = 6 us (DD)            significant
	//       encoding 1000 -> flux length = 8 us (max, DD)      ambivalent
	//       encoding 10   -> flux length = 2 us (min, HD)       significant
	//       encoding 100  -> flux length = 3 us (max, HD)       significant

	// If we have MFM, we should very soon detect a flux length of 6 us.
	// But if we have FM, how long should we search to be sure?
	// We assume that after 2000 us we should have reached the first IDAM,
	// which contains a sequence 1001, implying a flux length of 6 us.
	// If there was no such flux in that area, this can safely be assumed to be FM.

	// Do it only for the first track; the format only supports one encoding.
	if (encoding == UNKNOWN_ENCODING)
	{
		bool mfm_recording = false;
		int time0 = 0;
		int minflux = 4000;
		int fluxlen = 0;
		// Skip the beginning (may have a short cell)
		for (int i=2; (i < tbuf.size()-1) && (time0 < 2000000) && !mfm_recording; i++)
		{
			time0 = tbuf[i] & floppy_image::TIME_MASK;
			fluxlen = (tbuf[i+1] & floppy_image::TIME_MASK) - time0;
			if ((fluxlen < 3500) || (fluxlen > 5500 && fluxlen < 6500))
				mfm_recording = true;
			if (fluxlen < minflux) minflux = fluxlen;
		}
		encoding = mfm_recording? ISOIBM_MFM_ENCODING : ISOIBM_FM_ENCODING;

		// samplelength = 1000ns => 10^6 cells/sec => 500 kbit/s
		// samplelength = 2000ns => 250 kbit/s
		// We stay with double sampling at 250 kbit/s for FM
		if (minflux < 3500) samplelength = 1000;
		else samplelength = 2000;
	}

	// Start at the write splice
	uint32_t splice = image->get_write_splice_position(cyl, head, 0);

	int cur_pos = splice;
	int cur_entry = 0;

	// Fast-forward to the write splice position (always 0 in this format)
	while (cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry+1] & floppy_image::TIME_MASK) < cur_pos)
		cur_entry++;

	int period = samplelength;
	int period_adjust_base = period * 0.05;

	int min_period = int(samplelength*0.75);
	int max_period = int(samplelength*1.25);
	int phase_adjust = 0;
	int freq_hist = 0;
	uint32_t next = 0;

	int offset = 0x100;

	// Prepare offset for the format storage
	if (head==0)
	{
		offset = 0;
		track_end -= 0x0100;
	}

	uint8_t bit = 0x01;
	uint8_t current = 0;

	while (next < 200000000) {
		int edge = tbuf[cur_entry] & floppy_image::TIME_MASK;

		// Start of track? Use next entry.
		if (edge==0)
		{
			edge = tbuf[++cur_entry] & floppy_image::TIME_MASK;
		}

		// Wrapped over end?
		if (edge < cur_pos) edge += 200000000;

		// End of cell
		next = cur_pos + period + phase_adjust;

		// End of the window is at next; edge is the actual transition
		if (edge >= next)
		{
			// No transition in the window -> 0
			phase_adjust = 0;
		}
		else
		{
			// Transition in the window -> 1
			current |= bit;
			int delta = edge - (next - period/2);

			phase_adjust = 0.65*delta;

			if (delta < 0)
			{
				if (freq_hist < 0) freq_hist--;
				else freq_hist = -1;
			}
			else
			{
				if (delta > 0)
				{
					if(freq_hist > 0) freq_hist++;
					else freq_hist = 1;
				}
				else freq_hist = 0;
			}

			if (freq_hist)
			{
				int afh = freq_hist < 0 ? -freq_hist : freq_hist;
				if (afh > 1)
				{
					int aper = period_adjust_base*delta/period;
					if (!aper)
						aper = freq_hist < 0 ? -1 : 1;
					period += aper;

					if (period < min_period) period = min_period;
					else if (period > max_period) period = max_period;
				}
			}
		}

		cur_pos = next;

		bit = (bit << 1) & 0xff;
		if (bit == 0)
		{
			bit = 0x01;
			cylinder_buffer[offset++] = current;
			if ((offset & 0xff)==0) offset += 0x100;
			current = 0;
		}

		// Fast-forward to next cell
		while (cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
			cur_entry++;

		// Reaching the end of the track
		if (cur_entry == int(tbuf.size())-1 &&  (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
		{
			// Wrap to index 0 or 1 depending on whether there is a transition exactly at the index hole
			cur_entry = (tbuf[int(tbuf.size())-1] & floppy_image::MG_MASK) != (tbuf[0] & floppy_image::MG_MASK) ?
			0 : 1;
		}
	}
	// Write the current byte when not done
	if (bit != 0x01)
		cylinder_buffer[offset] = current;
}

const floppy_format_type FLOPPY_HFE_FORMAT = &floppy_image_format_creator<hfe_format>;


