OSDN Git Service

[VM][General] Merge Upstream 20180530.
[csp-qt/common_source_project-fm7.git] / source / src / vm / disk.cpp
index 06c726f..5c20d6b 100644 (file)
-/*\r
-       Skelton for retropc emulator\r
-\r
-       Author : Takeda.Toshiya\r
-       Date   : 2006.09.16-\r
-\r
-       [ d88 handler ]\r
-*/\r
-\r
-#include "disk.h"\r
-#include "../config.h"\r
-\r
-// crc table\r
-static const uint16 crc_table[256] = {\r
-       0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,\r
-       0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,\r
-       0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,\r
-       0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,\r
-       0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,\r
-       0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,\r
-       0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,\r
-       0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,\r
-       0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,\r
-       0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,\r
-       0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,\r
-       0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,\r
-       0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,\r
-       0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,\r
-       0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,\r
-       0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0\r
-};\r
-\r
-// teledisk decoder table\r
-static const uint8 d_code[256] = {\r
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-       0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-       0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,\r
-       0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,\r
-       0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,\r
-       0x18, 0x18, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x1f,\r
-       0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,\r
-       0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f,\r
-       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f\r
-};\r
-static const uint8 d_len[256] = {\r
-       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08\r
-};\r
-static const int secsize[8] = {\r
-       128, 256, 512, 1024, 2048, 4096, 8192, 16384\r
-};\r
-\r
-static uint8 tmp_buffer[DISK_BUFFER_SIZE];\r
-\r
-typedef struct {\r
-       int type;\r
-       int ncyl, nside, nsec, size;\r
-} fd_format_t;\r
-\r
-static const fd_format_t fd_formats[] = {\r
-       { MEDIA_TYPE_2D,  40, 1, 16,  256 },    // 1D   160KB\r
-#if defined(SUPPORT_MEDIA_TYPE_1DD)\r
-       { MEDIA_TYPE_2DD, 80, 1, 16,  256 },    // 1DD  320KB\r
-       { MEDIA_TYPE_2DD, 80, 1,  9,  512 },    // 1DD  360KB\r
-#else\r
-       { MEDIA_TYPE_2D , 40, 2, 16,  256 },    // 2D   320KB\r
-       { MEDIA_TYPE_2D,  40, 2,  9,  512 },    // 2D   360KB\r
-#endif\r
-#if defined(_MZ80B) || defined(_MZ2000) || defined(_MZ2200) || defined(_MZ2500)\r
-       { MEDIA_TYPE_2DD, 80, 2, 16,  256 },    // 2DD  640KB (MZ-2500)\r
-#else\r
-       { MEDIA_TYPE_2DD, 80, 2,  8,  512 },    // 2DD  640KB\r
-#endif\r
-       { MEDIA_TYPE_2DD, 80, 2,  9,  512 },    // 2DD  720KB\r
-#if defined(_PX7) || defined(_MSX1) || defined(_MSX2)\r
-       { MEDIA_TYPE_2DD, 81, 2,  9,  512 },    // 2DD  729KB\r
-#endif\r
-       { MEDIA_TYPE_2HD, 80, 2, 15,  512 },    // 2HC 1.20MB\r
-       { MEDIA_TYPE_2HD, 77, 2,  8, 1024 },    // 2HD 1.25MB\r
-       { MEDIA_TYPE_144, 80, 2, 18,  512 },    // 2HD 1.44MB\r
-       { MEDIA_TYPE_144, 80, 2, 36,  512 },    // 2ED 2.88MB\r
-       { -1, 0, 0, 0, 0 },\r
-};\r
-\r
-#define IS_VALID_TRACK(offset) ((offset) >= 0x20 && (offset) < sizeof(buffer))\r
-\r
-void DISK::open(_TCHAR path[], int bank)\r
-{\r
-       // check current disk image\r
-       if(inserted) {\r
-               if(_tcsicmp(orig_path, path) == 0 && file_bank == bank) {\r
-                       return;\r
-               }\r
-               close();\r
-       }\r
-       memset(buffer, 0, sizeof(buffer));\r
-       media_type = MEDIA_TYPE_UNK;\r
-       is_standard_image = is_fdi_image = false;\r
-       trim_required = false;\r
-       \r
-       // open disk image\r
-       fi = new FILEIO();\r
-       if(fi->Fopen(path, FILEIO_READ_BINARY)) {\r
-               bool converted = false;\r
-               \r
-               _tcscpy_s(orig_path, _MAX_PATH, path);\r
-               _tcscpy_s(dest_path, _MAX_PATH, path);\r
-               _stprintf_s(temp_path, _MAX_PATH, _T("%s.$$$"), path);\r
-               temporary = false;\r
-               \r
-               // check if file protected\r
-               write_protected = fi->IsProtected(path);\r
-               \r
-               // is this d88 format ?\r
-               if(check_file_extension(path, _T(".d88")) || check_file_extension(path, _T(".d77"))) {\r
-                       uint32 offset = 0;\r
-                       for(int i = 0; i < bank; i++) {\r
-                               fi->Fseek(offset + 0x1c, SEEK_SET);\r
-                               offset += fi->FgetUint32_LE();\r
-                       }\r
-                       fi->Fseek(offset + 0x1c, FILEIO_SEEK_SET);\r
-                       file_size.d = fi->FgetUint32_LE();\r
-                       fi->Fseek(offset, FILEIO_SEEK_SET);\r
-                       fi->Fread(buffer, file_size.d, 1);\r
-                       file_bank = bank;\r
-                       inserted = changed = true;\r
-//                     trim_required = true;\r
-                       \r
-                       // fix sector number from big endian to little endian\r
-                       for(int trkside = 0; trkside < 164; trkside++) {\r
-                               pair offset;\r
-                               offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);\r
-                               \r
-                               if(!IS_VALID_TRACK(offset.d)) {\r
-                                       break;\r
-                               }\r
-                               uint8* t = buffer + offset.d;\r
-                               pair sector_num, data_size;\r
-                               sector_num.read_2bytes_le_from(t + 4);\r
-                               bool is_be = (sector_num.b.l == 0 && sector_num.b.h >= 4);\r
-                               if(is_be) {\r
-                                       sector_num.read_2bytes_be_from(t + 4);\r
-                                       sector_num.write_2bytes_le_to(t + 4);\r
-                               }\r
-                               for(int i = 0; i < sector_num.sd; i++) {\r
-                                       if(is_be) {\r
-                                               sector_num.write_2bytes_le_to(t + 4);\r
-                                       }\r
-                                       data_size.read_2bytes_le_from(t + 14);\r
-                                       t += data_size.sd + 0x10;\r
-                               }\r
-                       }\r
-                       goto file_loaded;\r
-               }\r
-               \r
-               fi->Fseek(0, FILEIO_SEEK_END);\r
-               file_size.d = fi->Ftell();\r
-               fi->Fseek(0, FILEIO_SEEK_SET);\r
-               file_bank = 0;\r
-               \r
-#if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)\r
-               // is this 2d format ?\r
-               if(check_file_extension(path, _T(".2d"))) {\r
-                       if(standard_to_d88(MEDIA_TYPE_2D, 40, 2, 16, 256)) {\r
-                               inserted = changed = is_standard_image = true;\r
-                               goto file_loaded;\r
-                       }\r
-                       fi->Fseek(0, FILEIO_SEEK_SET);\r
-               }\r
-#endif\r
-               \r
-               // check image file format\r
-               for(int i = 0;; i++) {\r
-                       const fd_format_t *p = &fd_formats[i];\r
-                       if(p->type == -1) {\r
-                               break;\r
-                       }\r
-                       int len = p->ncyl * p->nside * p->nsec * p->size;\r
-                       // 4096 bytes: FDI header ???\r
-                       if(file_size.d == len || (file_size.d == (len + 4096) && (len == 655360 || len == 1261568))) {\r
-                               if(file_size.d == len + 4096) {\r
-                                       is_fdi_image = true;\r
-                                       fi->Fread(fdi_header, 4096, 1);\r
-                               }\r
-                               if(standard_to_d88(p->type, p->ncyl, p->nside, p->nsec, p->size)) {\r
-                                       inserted = changed = is_standard_image = true;\r
-                                       goto file_loaded;\r
-                               }\r
-                       }\r
-               }\r
-               if(0 < file_size.d && file_size.d <= DISK_BUFFER_SIZE) {\r
-                       memset(buffer, 0, sizeof(buffer));\r
-                       fi->Fread(buffer, file_size.d, 1);\r
-                       \r
-                       // check d88 format (temporary)\r
-                       if(file_size.b.l == buffer[0x1c] && file_size.b.h == buffer[0x1d] && file_size.b.h2 == buffer[0x1e] && file_size.b.h3 == buffer[0x1f]) {\r
-                               inserted = changed = true;\r
-                               goto file_loaded;\r
-                       }\r
-                       _stprintf_s(dest_path, _MAX_PATH, _T("%s.D88"), path);\r
-                       \r
-                       // check file header\r
-                       try {\r
-                               if(memcmp(buffer, "TD", 2) == 0 || memcmp(buffer, "td", 2) == 0) {\r
-                                       // teledisk image file\r
-                                       inserted = changed = converted = teledisk_to_d88();\r
-                               } else if(memcmp(buffer, "IMD", 3) == 0) {\r
-                                       // imagedisk image file\r
-                                       inserted = changed = converted = imagedisk_to_d88();\r
-                               } else if(memcmp(buffer, "MV - CPC", 8) == 0) {\r
-                                       // standard cpdread image file\r
-                                       inserted = changed = converted = cpdread_to_d88(0);\r
-                               } else if(memcmp(buffer, "EXTENDED", 8) == 0) {\r
-                                       // extended cpdread image file\r
-                                       inserted = changed = converted = cpdread_to_d88(1);\r
-                               }\r
-                       } catch(...) {\r
-                               // failed to convert the disk image\r
-                       }\r
-               }\r
-file_loaded:\r
-               if(fi->IsOpened()) {\r
-                       fi->Fclose();\r
-               }\r
-               if(temporary) {\r
-                       fi->Remove(temp_path);\r
-               }\r
-               if(inserted) {\r
-#if 0\r
-                       if(converted) {\r
-                               // write image\r
-                               FILEIO* fio = new FILEIO();\r
-                               if(fio->Fopen(dest_path, FILEIO_WRITE_BINARY)) {\r
-                                       fio->Fwrite(buffer, file_size.d, 1);\r
-                                       fio->Fclose();\r
-                               }\r
-                               delete fio;\r
-                       }\r
-#endif\r
-                       crc32 = getcrc32(buffer, file_size.d);\r
-               }\r
-               if(buffer[0x1a] != 0) {\r
-                       write_protected = true;\r
-               }\r
-               if(media_type == MEDIA_TYPE_UNK) {\r
-                       if((media_type = buffer[0x1b]) == MEDIA_TYPE_2HD) {\r
-                               for(int trkside = 0; trkside < 164; trkside++) {\r
-                                       pair offset;\r
-                                       offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);\r
-                                       \r
-                                       if(!IS_VALID_TRACK(offset.d)) {\r
-                                               continue;\r
-                                       }\r
-                                       // track found\r
-                                       uint8 *t = buffer + offset.d;\r
-                                       pair sector_num, data_size;\r
-                                       sector_num.read_2bytes_le_from(t + 4);\r
-                                       data_size.read_2bytes_le_from(t + 14);\r
-                                       \r
-                                       if(sector_num.sd >= 18 && data_size.sd == 512) {\r
-                                               media_type = MEDIA_TYPE_144;\r
-                                       }\r
-                                       break;\r
-                               }\r
-                       }\r
-               }\r
-               // FIXME: ugly patch for X1turbo ALPHA and Batten Tanuki\r
-               is_special_disk = 0;\r
-#if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)\r
-               if(media_type == MEDIA_TYPE_2D) {\r
-                       // check first sector\r
-                       pair offset;\r
-                       offset.read_4bytes_le_from(buffer + 0x20);\r
-                       if(IS_VALID_TRACK(offset.d)) {\r
-                               static const uint8 batten[] = {0xca, 0xde, 0xaf, 0xc3, 0xdd, 0x20, 0xc0, 0xc7, 0xb7};\r
-                               uint8 *t = buffer + offset.d;\r
-                               if(strncmp((char *)(t + 0x11), "turbo ALPHA", 11) == 0) {\r
-                                       is_special_disk = SPECIAL_DISK_X1_ALPHA;\r
-                               } else if(memcmp((void *)(t + 0x11), batten, sizeof(batten)) == 0) {\r
-                                       is_special_disk = SPECIAL_DISK_X1_BATTEN;\r
-                               }\r
-                       }\r
-               }\r
-#endif\r
-       }\r
-       delete fi;\r
-}\r
-\r
-void DISK::close()\r
-{\r
-       // write disk image\r
-       if(inserted) {\r
-               if(trim_required) {\r
-                       trim_buffer();\r
-                       trim_required = false;\r
-               }\r
-               if(!write_protected && file_size.d && getcrc32(buffer, file_size.d) != crc32) {\r
-                       // write image\r
-                       FILEIO* fio = new FILEIO();\r
-                       int pre_size = 0, post_size = 0;\r
-                       uint8 *pre_buffer = NULL, *post_buffer = NULL;\r
-                       \r
-                       // is this d88 format ?\r
-                       if(check_file_extension(dest_path, _T(".d88")) || check_file_extension(dest_path, _T(".d77"))) {\r
-                               if(fio->Fopen(dest_path, FILEIO_READ_BINARY)) {\r
-                                       fio->Fseek(0, FILEIO_SEEK_END);\r
-                                       uint32 total_size = fio->Ftell(), offset = 0;\r
-                                       for(int i = 0; i < file_bank; i++) {\r
-                                               fio->Fseek(offset + 0x1c, SEEK_SET);\r
-                                               offset += fio->FgetUint32_LE();\r
-                                       }\r
-                                       if((pre_size = offset) > 0) {\r
-                                               pre_buffer = (uint8 *)malloc(pre_size);\r
-                                               fio->Fseek(0, FILEIO_SEEK_SET);\r
-                                               fio->Fread(pre_buffer, pre_size, 1);\r
-                                       }\r
-                                       fio->Fseek(offset + 0x1c, SEEK_SET);\r
-                                       offset += fio->FgetUint32_LE();\r
-                                       if((post_size = total_size - offset) > 0) {\r
-                                               post_buffer = (uint8 *)malloc(post_size);\r
-                                               fio->Fseek(offset, FILEIO_SEEK_SET);\r
-                                               fio->Fread(post_buffer, post_size, 1);\r
-                                       }\r
-                                       fio->Fclose();\r
-                               }\r
-                       }\r
-                       if(!fio->Fopen(dest_path, FILEIO_WRITE_BINARY)) {\r
-                               _TCHAR tmp_path[_MAX_PATH];\r
-                               _stprintf_s(tmp_path, _MAX_PATH, _T("temporary_saved_floppy_disk_#%d.d88"), drive_num);\r
-                               fio->Fopen(emu->bios_path(tmp_path), FILEIO_WRITE_BINARY);\r
-                       }\r
-                       if(fio->IsOpened()) {\r
-                               if(pre_buffer) {\r
-                                       fio->Fwrite(pre_buffer, pre_size, 1);\r
-                               }\r
-                               if(is_standard_image) {\r
-                                       if(is_fdi_image) {\r
-                                               fio->Fwrite(fdi_header, 4096, 1);\r
-                                       }\r
-                                       for(int trkside = 0; trkside < 164; trkside++) {\r
-                                               pair offset;\r
-                                               offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);\r
-                                               \r
-                                               if(!IS_VALID_TRACK(offset.d)) {\r
-                                                       break;\r
-                                               }\r
-                                               uint8* t = buffer + offset.d;\r
-                                               pair sector_num, data_size;\r
-                                               sector_num.read_2bytes_le_from(t + 4);\r
-                                               \r
-                                               for(int i = 0; i < sector_num.sd; i++) {\r
-                                                       data_size.read_2bytes_le_from(t + 14);\r
-                                                       fio->Fwrite(t + 0x10, data_size.sd, 1);\r
-                                                       t += data_size.sd + 0x10;\r
-                                               }\r
-                                       }\r
-                               } else {\r
-                                       fio->Fwrite(buffer, file_size.d, 1);\r
-                               }\r
-                               if(post_buffer) {\r
-                                       fio->Fwrite(post_buffer, post_size, 1);\r
-                               }\r
-                               fio->Fclose();\r
-                       }\r
-                       if(pre_buffer) {\r
-                               free(pre_buffer);\r
-                       }\r
-                       if(post_buffer) {\r
-                               free(post_buffer);\r
-                       }\r
-                       delete fio;\r
-               }\r
-               ejected = true;\r
-       }\r
-       inserted = write_protected = false;\r
-       file_size.d = 0;\r
-       sector_size.sd = sector_num.sd = 0;\r
-       sector = NULL;\r
-}\r
-\r
-bool DISK::get_track(int trk, int side)\r
-{\r
-       sector_size.sd = sector_num.sd = 0;\r
-       invalid_format = false;\r
-       no_skew = true;\r
-       \r
-       // disk not inserted or invalid media type\r
-       if(!(inserted && check_media_type())) {\r
-               return false;\r
-       }\r
-       \r
-       // search track\r
-       int trkside = trk * 2 + (side & 1);\r
-       if(!(0 <= trkside && trkside < 164)) {\r
-               return false;\r
-       }\r
-       cur_track = trk;\r
-       cur_side = side;\r
-       \r
-       pair offset;\r
-       offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);\r
-       \r
-       if(!IS_VALID_TRACK(offset.d)) {\r
-               return false;\r
-       }\r
-       \r
-       // track found\r
-       sector = buffer + offset.d;\r
-       sector_num.read_2bytes_le_from(sector + 4);\r
-       pair data_size;\r
-       data_size.read_2bytes_le_from(sector + 14);\r
-       \r
-       // create each sector position in track\r
-       int sync_size  = drive_mfm ? 12 : 6;\r
-       int am_size = drive_mfm ? 3 : 0;\r
-       int gap0_size = drive_mfm ? 80 : 40;\r
-       int gap1_size = drive_mfm ? 50 : 26;\r
-       int gap2_size = drive_mfm ? 22 : 11;\r
-       int gap3_size = 0, gap4_size;\r
-       \r
-       if(media_type == MEDIA_TYPE_144 || media_type == MEDIA_TYPE_2HD) {\r
-               if(drive_mfm) {\r
-                       if(data_size.sd ==  256 && sector_num.sd == 26) gap3_size =  54;\r
-                       if(data_size.sd ==  512 && sector_num.sd == 15) gap3_size =  84;\r
-                       if(data_size.sd == 1024 && sector_num.sd ==  8) gap3_size = 116;\r
-               } else {\r
-                       if(data_size.sd ==  128 && sector_num.sd == 26) gap3_size =  27;\r
-                       if(data_size.sd ==  256 && sector_num.sd == 15) gap3_size =  42;\r
-                       if(data_size.sd ==  512 && sector_num.sd ==  8) gap3_size =  58;\r
-               }\r
-       } else {\r
-               if(drive_mfm) {\r
-                       if(data_size.sd ==  256 && sector_num.sd == 16) gap3_size =  51;\r
-                       if(data_size.sd ==  512 && sector_num.sd ==  9) gap3_size =  80;\r
-                       if(data_size.sd == 1024 && sector_num.sd ==  5) gap3_size = 116;\r
-               } else {\r
-                       if(data_size.sd ==  128 && sector_num.sd == 16) gap3_size =  27;\r
-                       if(data_size.sd ==  256 && sector_num.sd ==  9) gap3_size =  42;\r
-                       if(data_size.sd ==  512 && sector_num.sd ==  5) gap3_size =  58;\r
-               }\r
-       }\r
-       \r
-       uint8* t = sector;\r
-       int total = sync_size + (am_size + 1);\r
-       \r
-       for(int i = 0; i < sector_num.sd; i++) {\r
-               data_size.read_2bytes_le_from(t + 14);\r
-               total += sync_size + (am_size + 1) + (4 + 2) + gap2_size + sync_size + (am_size + 1);\r
-               total += data_size.sd + 2;\r
-               if(t[2] != i + 1) {\r
-                       no_skew = false;\r
-               }\r
-               t += data_size.sd + 0x10;\r
-       }\r
-       if(gap3_size == 0) {\r
-               gap3_size = (get_track_size() - total - gap0_size - gap1_size) / (sector_num.sd + 1);\r
-       }\r
-       gap4_size = get_track_size() - total - gap0_size - gap1_size - gap3_size * sector_num.sd;\r
-       \r
-       if(gap3_size < 8 || gap4_size < 8) {\r
-               gap0_size = gap1_size = gap3_size = (get_track_size() - total) / (2 + sector_num.sd + 1);\r
-               gap4_size = get_track_size() - total - gap0_size - gap1_size - gap3_size * sector_num.sd;\r
-       }\r
-       if(gap3_size < 8 || gap4_size < 8) {\r
-               gap0_size = gap1_size = gap3_size = gap4_size = 32;\r
-               invalid_format = true;\r
-       }\r
-       int preamble_size = gap0_size + sync_size + (am_size + 1) + gap1_size;\r
-       \r
-       t = sector;\r
-       total = preamble_size;\r
-       sync_position[array_length(sync_position) - 1] = gap0_size; // sync position in preamble\r
-       \r
-       for(int i = 0; i < sector_num.sd; i++) {\r
-               data_size.read_2bytes_le_from(t + 14);\r
-               if(invalid_format) {\r
-                       total = preamble_size + (get_track_size() - preamble_size - gap4_size) * i / sector_num.sd;\r
-               }\r
-               sync_position[i] = total;\r
-               total += sync_size + (am_size + 1);\r
-               id_position[i] = total;\r
-               total += (4 + 2) + gap2_size + sync_size + (am_size + 1);\r
-               data_position[i] = total;\r
-               total += data_size.sd + 2 + gap3_size;\r
-               t += data_size.sd + 0x10;\r
-       }\r
-       return true;\r
-}\r
-\r
-bool DISK::make_track(int trk, int side)\r
-{\r
-       int track_size = get_track_size();\r
-       \r
-       if(!get_track(trk, side)) {\r
-               // create a dummy track\r
-               for(int i = 0; i < track_size; i++) {\r
-                       track[i] = rand();\r
-               }\r
-               return false;\r
-       }\r
-       \r
-       // make track image\r
-       int sync_size  = drive_mfm ? 12 : 6;\r
-       int am_size = drive_mfm ? 3 : 0;\r
-       int gap2_size = drive_mfm ? 22 : 11;\r
-       uint8 gap_data = drive_mfm ? 0x4e : 0xff;\r
-       \r
-       // preamble\r
-       memset(track, gap_data, track_size);\r
-       int q = sync_position[array_length(sync_position) - 1];\r
-       \r
-       // sync\r
-       for(int i = 0; i < sync_size; i++) {\r
-               track[q++] = 0x00;\r
-       }\r
-       // index mark\r
-       for(int i = 0; i < am_size; i++) {\r
-               track[q++] = 0xc2;\r
-       }\r
-       track[q++] = 0xfc;\r
-       \r
-       // sectors\r
-       uint8 *t = sector;\r
-       \r
-       for(int i = 0; i < sector_num.sd; i++) {\r
-               pair data_size;\r
-               data_size.read_2bytes_le_from(t + 14);\r
-               int p = sync_position[i];\r
-               \r
-               // sync\r
-               for(int j = 0; j < sync_size; j++) {\r
-                       if(p < track_size) track[p++] = 0x00;\r
-               }\r
-               // am1\r
-               for(int j = 0; j < am_size; j++) {\r
-                       if(p < track_size) track[p++] = 0xa1;\r
-               }\r
-               if(p < track_size) track[p++] = 0xfe;\r
-               // id\r
-               if(p < track_size) track[p++] = t[0];\r
-               if(p < track_size) track[p++] = t[1];\r
-               if(p < track_size) track[p++] = t[2];\r
-               if(p < track_size) track[p++] = t[3];\r
-               uint16 crc = 0;\r
-               crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0]]);\r
-               crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[1]]);\r
-               crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[2]]);\r
-               crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[3]]);\r
-               if(p < track_size) track[p++] = (crc >> 8) & 0xff;\r
-               if(p < track_size) track[p++] = (crc >> 0) & 0xff;\r
-               // gap2\r
-               for(int j = 0; j < gap2_size; j++) {\r
-                       if(p < track_size) track[p++] = gap_data;\r
-               }\r
-               // sync\r
-               for(int j = 0; j < sync_size; j++) {\r
-                       if(p < track_size) track[p++] = 0x00;\r
-               }\r
-               // am2\r
-               for(int j = 0; j < am_size; j++) {\r
-                       if(p < track_size) track[p++] = 0xa1;\r
-               }\r
-               if(p < track_size) track[p++] = (t[7] != 0) ? 0xf8 : 0xfb;\r
-               // data\r
-               crc = 0;\r
-               for(int j = 0; j < data_size.sd; j++) {\r
-                       if(p < track_size) track[p++] = t[0x10 + j];\r
-                       crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0x10 + j]]);\r
-               }\r
-               if(p < track_size) track[p++] = (crc >> 8) & 0xff;\r
-               if(p < track_size) track[p++] = (crc >> 0) & 0xff;\r
-               \r
-               t += data_size.sd + 0x10;\r
-       }\r
-       return true;\r
-}\r
-\r
-bool DISK::get_sector(int trk, int side, int index)\r
-{\r
-       sector_size.sd = sector_num.sd = 0;\r
-       sector = NULL;\r
-       \r
-       // disk not inserted or invalid media type\r
-       if(!(inserted && check_media_type())) {\r
-               return false;\r
-       }\r
-       \r
-       // search track\r
-       if(trk == -1 && side == -1) {\r
-               trk = cur_track;\r
-               side = cur_side;\r
-       }\r
-       int trkside = trk * 2 + (side & 1);\r
-       if(!(0 <= trkside && trkside < 164)) {\r
-               return false;\r
-       }\r
-       pair offset;\r
-       offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);\r
-       \r
-       if(!IS_VALID_TRACK(offset.d)) {\r
-               return false;\r
-       }\r
-       \r
-       // track found\r
-       uint8* t = buffer + offset.d;\r
-       sector_num.read_2bytes_le_from(t + 4);\r
-       \r
-       if(index >= sector_num.sd) {\r
-               return false;\r
-       }\r
-       \r
-       // skip sector\r
-       for(int i = 0; i < index; i++) {\r
-               pair data_size;\r
-               data_size.read_2bytes_le_from(t + 14);\r
-               t += data_size.sd + 0x10;\r
-       }\r
-       set_sector_info(t);\r
-       return true;\r
-}\r
-\r
-void DISK::set_sector_info(uint8 *t)\r
-{\r
-       // header info\r
-       id[0] = t[0];\r
-       id[1] = t[1];\r
-       id[2] = t[2];\r
-       id[3] = t[3];\r
-       uint16 crc = 0;\r
-       crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0]]);\r
-       crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[1]]);\r
-       crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[2]]);\r
-       crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[3]]);\r
-       id[4] = (crc >> 8) & 0xff;\r
-       id[5] = (crc >> 0) & 0xff;\r
-       // http://www,gnu-darwin.or.jp/www001/src/ports/emulators/quasi88/work/quasi88-0.6.3/document/FORMAT.TXT\r
-       // t[6]: 0x00 = double-density, 0x40 = single-density\r
-       // t[7]: 0x00 = normal, 0x10 = deleted mark\r
-       // t[8]: 0x00 = valid, 0x10 = valid (deleted data), 0xa0 = id crc error, 0xb0 = data crc error, 0xe0 = address mark missing, 0xf0 = data mark missing\r
-       density = t[6];\r
-       deleted = (t[7] != 0);\r
-   \r
-       if(config.ignore_crc != false) {\r
-               crc_error = false;\r
-       } else {\r
-               crc_error = (t[8] != 0x00 && t[8] != 0x10);\r
-       }\r
-       sector = t + 0x10;\r
-       sector_size.read_2bytes_le_from(t + 14);\r
-}\r
-\r
-void DISK::set_deleted(bool value)\r
-{\r
-       if(sector != NULL) {\r
-               uint8 *t = sector - 0x10;\r
-               t[7] = value ? 0x10 : 0;\r
-               if(t[8] == 0x00 || t[8] == 0x10) {\r
-                       t[8] = t[7];\r
-               }\r
-       }\r
-       deleted = value;\r
-}\r
-\r
-void DISK::set_crc_error(bool value)\r
-{\r
-       if(sector != NULL) {\r
-               uint8 *t = sector - 0x10;\r
-               t[8] = value ? 0xb0 : t[7]; // FIXME: always data crc error ?\r
-       }\r
-       crc_error = value;\r
-}\r
-\r
-bool DISK::format_track(int trk, int side)\r
-{\r
-       // disk not inserted or invalid media type\r
-       if(!(inserted && check_media_type())) {\r
-               return false;\r
-       }\r
-       \r
-       // search track\r
-       int trkside = trk * 2 + (side & 1);\r
-       if(!(0 <= trkside && trkside < 164)) {\r
-               return false;\r
-       }\r
-       \r
-       // create new empty track\r
-       if(trim_required) {\r
-               trim_buffer();\r
-               trim_required = false;\r
-       }\r
-       memset(buffer + DISK_BUFFER_SIZE, 0, sizeof(buffer) - DISK_BUFFER_SIZE);\r
-       pair offset;\r
-       offset.d = DISK_BUFFER_SIZE;\r
-       offset.write_4bytes_le_to(buffer + 0x20 + trkside * 4);\r
-       \r
-       trim_required = true;\r
-       sector_num.sd = 0;\r
-       return true;\r
-}\r
-\r
-void DISK::insert_sector(uint8 c, uint8 h, uint8 r, uint8 n, bool deleted, bool crc_error, uint8 fill_data, int length)\r
-{\r
-       uint8* t = buffer + DISK_BUFFER_SIZE;\r
-       \r
-       sector_num.sd++;\r
-       for(int i = 0; i < (sector_num.sd - 1); i++) {\r
-               t[4] = sector_num.b.l;\r
-               t[5] = sector_num.b.h;\r
-               pair data_size;\r
-               data_size.read_2bytes_le_from(t + 14);\r
-               t += data_size.sd + 0x10;\r
-       }\r
-       t[0] = c;\r
-       t[1] = h;\r
-       t[2] = r;\r
-       t[3] = n;\r
-       t[4] = sector_num.b.l;\r
-       t[5] = sector_num.b.h;\r
-       t[6] = drive_mfm ? 0 : 0x40;\r
-       t[7] = deleted ? 0x10 : 0;\r
-       t[8] = crc_error ? 0xb0 : t[7]; // FIXME: always data crc error ?\r
-       t[14] = (length >> 0) & 0xff;\r
-       t[15] = (length >> 8) & 0xff;\r
-       memset(t + 16, fill_data, length);\r
-       \r
-       set_sector_info(t);\r
-}\r
-\r
-void DISK::sync_buffer()\r
-{\r
-       if(trim_required) {\r
-               trim_buffer();\r
-               trim_required = false;\r
-       }\r
-}\r
-\r
-void DISK::trim_buffer()\r
-{\r
-       int max_tracks = 164;\r
-       uint32 dest_offset = 0x2b0;\r
-       \r
-       // copy header\r
-       memset(tmp_buffer, 0, sizeof(tmp_buffer));\r
-       memcpy(tmp_buffer, buffer, 0x20);\r
-       \r
-       // check max tracks\r
-       for(int trkside = 0; trkside < 164; trkside++) {\r
-               pair src_trk_offset;\r
-               src_trk_offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);\r
-               if(src_trk_offset.d != 0) {\r
-#if 1\r
-                       if(src_trk_offset.d < 0x2b0) {\r
-                               max_tracks = (src_trk_offset.d - 0x20) >> 2;\r
-                       }\r
-#else\r
-                       if(src_trk_offset.d != 0x2b0) {\r
-                               max_tracks = (src_trk_offset.d - 0x20) >> 2;\r
-                               if(max_tracks > 164) {\r
-                                       dest_offset = 0x20 + max_tracks * 4);\r
-                               }\r
-                       }\r
-#endif\r
-                       break;\r
-               }\r
-       }\r
-       \r
-       // copy tracks\r
-       for(int trkside = 0; trkside < max_tracks; trkside++) {\r
-               pair src_trk_offset;\r
-               src_trk_offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);\r
-               \r
-               pair dest_trk_offset;\r
-               dest_trk_offset.d = 0;\r
-               \r
-               if(IS_VALID_TRACK(src_trk_offset.d)) {\r
-                       uint8* t = buffer + src_trk_offset.d;\r
-                       pair sector_num, data_size;\r
-                       sector_num.read_2bytes_le_from(t + 4);\r
-                       if(sector_num.sd != 0) {\r
-                               dest_trk_offset.d = dest_offset;\r
-                               for(int i = 0; i < sector_num.sd; i++) {\r
-                                       data_size.read_2bytes_le_from(t + 14);\r
-                                       memcpy(tmp_buffer + dest_offset, t, data_size.sd + 0x10);\r
-                                       dest_offset += data_size.sd + 0x10;\r
-                                       t += data_size.sd + 0x10;\r
-                               }\r
-                       }\r
-               }\r
-               dest_trk_offset.write_4bytes_le_to(tmp_buffer + 0x20 + trkside * 4);\r
-       }\r
-       \r
-       // update file size\r
-       file_size.d = dest_offset;\r
-       file_size.write_4bytes_le_to(tmp_buffer + 0x1c);\r
-       \r
-       memset(buffer, 0, sizeof(buffer));\r
-       memcpy(buffer, tmp_buffer, file_size.d);\r
-}\r
-\r
-int DISK::get_rpm()\r
-{\r
-       if(drive_rpm != 0) {\r
-               return drive_rpm;\r
-       } else if(inserted) {\r
-               return (media_type == MEDIA_TYPE_2HD) ? 360 : 300;\r
-       } else {\r
-               return (drive_type == DRIVE_TYPE_2HD) ? 360 : 300;\r
-       }\r
-}\r
-\r
-int DISK::get_track_size()\r
-{\r
-       if(inserted) {\r
-               return media_type == MEDIA_TYPE_144 ? 12500 : media_type == MEDIA_TYPE_2HD ? 10410 : drive_mfm ? 6250 : 3100;\r
-       } else {\r
-               return drive_type == DRIVE_TYPE_144 ? 12500 : drive_type == DRIVE_TYPE_2HD ? 10410 : drive_mfm ? 6250 : 3100;\r
-       }\r
-}\r
-\r
-double DISK::get_usec_per_bytes(int bytes)\r
-{\r
-       return 1000000.0 / (get_track_size() * (get_rpm() / 60.0)) * bytes;\r
-}\r
-\r
-bool DISK::check_media_type()\r
-{\r
-       switch(drive_type) {\r
-       case DRIVE_TYPE_2D:\r
-               return (media_type == MEDIA_TYPE_2D);\r
-       case DRIVE_TYPE_2DD:\r
-               return (media_type == MEDIA_TYPE_2D || media_type == MEDIA_TYPE_2DD);\r
-       case DRIVE_TYPE_2HD:\r
-               return (media_type == MEDIA_TYPE_2HD);\r
-       case DRIVE_TYPE_144:\r
-               return (media_type == MEDIA_TYPE_144);\r
-       case DRIVE_TYPE_UNK:\r
-               return true; // always okay\r
-       }\r
-       return false;\r
-}\r
-\r
-// teledisk image decoder\r
-\r
-/*\r
-       this teledisk image decoder is based on:\r
-       \r
-               LZHUF.C English version 1.0 based on Japanese version 29-NOV-1988\r
-               LZSS coded by Haruhiko OKUMURA\r
-               Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI\r
-               Edited and translated to English by Kenji RIKITAKE\r
-               TDLZHUF.C by WTK\r
-*/\r
-\r
-#define COPYBUFFER(src, size) { \\r
-       if(file_size.d + (size) > DISK_BUFFER_SIZE) { \\r
-               return false; \\r
-       } \\r
-       memcpy(buffer + file_size.d, (src), (size)); \\r
-       file_size.d += (size); \\r
-}\r
-\r
-bool DISK::teledisk_to_d88()\r
-{\r
-       td_hdr_t hdr;\r
-       td_cmt_t cmt;\r
-       td_trk_t trk;\r
-       td_sct_t sct;\r
-       d88_hdr_t d88_hdr;\r
-       d88_sct_t d88_sct;\r
-       uint8 obuf[512];\r
-       \r
-       // check teledisk header\r
-       fi->Fseek(0, FILEIO_SEEK_SET);\r
-       fi->Fread(&hdr, sizeof(td_hdr_t), 1);\r
-       if(hdr.sig[0] == 't' && hdr.sig[1] == 'd') {\r
-               // decompress to the temporary file\r
-               FILEIO* fo = new FILEIO();\r
-               if(!fo->Fopen(temp_path, FILEIO_WRITE_BINARY)) {\r
-                       delete fo;\r
-                       return false;\r
-               }\r
-               int rd = 1;\r
-               init_decode();\r
-               do {\r
-                       if((rd = decode(obuf, 512)) > 0) {\r
-                               fo->Fwrite(obuf, rd, 1);\r
-                       }\r
-               }\r
-               while(rd > 0);\r
-               fo->Fclose();\r
-               delete fo;\r
-               temporary = true;\r
-               \r
-               // reopen the temporary file\r
-               fi->Fclose();\r
-               if(!fi->Fopen(temp_path, FILEIO_READ_BINARY)) {\r
-                       return false;\r
-               }\r
-       }\r
-       if(hdr.flag & 0x80) {\r
-               // skip comment\r
-               fi->Fread(&cmt, sizeof(td_cmt_t), 1);\r
-               fi->Fseek(cmt.len, FILEIO_SEEK_CUR);\r
-       }\r
-       \r
-       // create d88 image\r
-       file_size.d = 0;\r
-       \r
-       // create d88 header\r
-       memset(&d88_hdr, 0, sizeof(d88_hdr_t));\r
-       _strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "TELEDISK");\r
-       d88_hdr.protect = 0; // non-protected\r
-       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));\r
-       \r
-       // create tracks\r
-       int trkcnt = 0, trkptr = sizeof(d88_hdr_t);\r
-       fi->Fread(&trk, sizeof(td_trk_t), 1);\r
-       while(trk.nsec != 0xff) {\r
-               d88_hdr.trkptr[trkcnt++] = trkptr;\r
-               if(hdr.sides == 1) {\r
-                       d88_hdr.trkptr[trkcnt++] = trkptr;\r
-               }\r
-               \r
-               // read sectors in this track\r
-               for(int i = 0; i < trk.nsec; i++) {\r
-                       uint8 buf[2048], dst[2048];\r
-                       memset(buf, 0, sizeof(buf));\r
-                       memset(dst, 0, sizeof(dst));\r
-                       \r
-                       // read sector header\r
-                       fi->Fread(&sct, sizeof(td_sct_t), 1);\r
-                       \r
-                       // create d88 sector header\r
-                       memset(&d88_sct, 0, sizeof(d88_sct_t));\r
-                       d88_sct.c = sct.c;\r
-                       d88_sct.h = sct.h;\r
-                       d88_sct.r = sct.r;\r
-                       d88_sct.n = sct.n;\r
-                       d88_sct.nsec = trk.nsec;\r
-                       d88_sct.dens = (hdr.dens & 0x80) ? 0x40 : 0;\r
-                       d88_sct.del = (sct.ctrl & 4) ? 0x10 : 0;\r
-                       d88_sct.stat = (sct.ctrl & 2) ? 0xb0 : d88_sct.del;\r
-                       d88_sct.size = secsize[sct.n & 3];\r
-                       \r
-                       // create sector image\r
-                       if(sct.ctrl != 0x10) {\r
-                               // read sector source\r
-                               int len = fi->Fgetc();\r
-                               len += fi->Fgetc() * 256 - 1;\r
-                               int flag = fi->Fgetc(), d = 0;\r
-                               fi->Fread(buf, len, 1);\r
-                               \r
-                               // convert\r
-                               if(flag == 0) {\r
-                                       memcpy(dst, buf, len);\r
-                               } else if(flag == 1) {\r
-                                       pair len2;\r
-                                       len2.read_2bytes_le_from(buf);\r
-                                       while(len2.sd--) {\r
-                                               dst[d++] = buf[2];\r
-                                               dst[d++] = buf[3];\r
-                                       }\r
-                               } else if(flag == 2) {\r
-                                       for(int s = 0; s < len;) {\r
-                                               int type = buf[s++];\r
-                                               int len2 = buf[s++];\r
-                                               if(type == 0) {\r
-                                                       while(len2--) {\r
-                                                               dst[d++] = buf[s++];\r
-                                                       }\r
-                                               } else if(type < 5) {\r
-                                                       uint8 pat[256];\r
-                                                       int n = 2;\r
-                                                       while(type-- > 1) {\r
-                                                               n *= 2;\r
-                                                       }\r
-                                                       for(int j = 0; j < n; j++) {\r
-                                                               pat[j] = buf[s++];\r
-                                                       }\r
-                                                       while(len2--) {\r
-                                                               for(int j = 0; j < n; j++) {\r
-                                                                       dst[d++] = pat[j];\r
-                                                               }\r
-                                                       }\r
-                                               } else {\r
-                                                       break; // unknown type\r
-                                               }\r
-                                       }\r
-                               } else {\r
-                                       break; // unknown flag\r
-                               }\r
-                       } else {\r
-                               d88_sct.size = 0;\r
-                       }\r
-                       \r
-                       // copy to d88\r
-                       COPYBUFFER(&d88_sct, sizeof(d88_sct_t));\r
-                       COPYBUFFER(dst, d88_sct.size);\r
-                       trkptr += sizeof(d88_sct_t) + d88_sct.size;\r
-               }\r
-               // read next track\r
-               fi->Fread(&trk, sizeof(td_trk_t), 1);\r
-       }\r
-       d88_hdr.type = ((hdr.dens & 3) == 2) ? MEDIA_TYPE_2HD : ((trkcnt >> 1) > 60) ? MEDIA_TYPE_2DD : MEDIA_TYPE_2D;\r
-       d88_hdr.size = trkptr;\r
-       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));\r
-       return true;\r
-}\r
-\r
-int DISK::next_word()\r
-{\r
-       if(ibufndx >= ibufcnt) {\r
-               ibufndx = ibufcnt = 0;\r
-               memset(inbuf, 0, 512);\r
-               for(int i = 0; i < 512; i++) {\r
-                       int d = fi->Fgetc();\r
-                       if(d == EOF) {\r
-                               if(i) {\r
-                                       break;\r
-                               }\r
-                               return(-1);\r
-                       }\r
-                       inbuf[i] = d;\r
-                       ibufcnt = i + 1;\r
-               }\r
-       }\r
-       while(getlen <= 8) {\r
-               getbuf |= inbuf[ibufndx++] << (8 - getlen);\r
-               getlen += 8;\r
-       }\r
-       return 0;\r
-}\r
-\r
-int DISK::get_bit()\r
-{\r
-       if(next_word() < 0) {\r
-               return -1;\r
-       }\r
-       short i = getbuf;\r
-       getbuf <<= 1;\r
-       getlen--;\r
-       return (i < 0) ? 1 : 0;\r
-}\r
-\r
-int DISK::get_byte()\r
-{\r
-       if(next_word() != 0) {\r
-               return -1;\r
-       }\r
-       uint16 i = getbuf;\r
-       getbuf <<= 8;\r
-       getlen -= 8;\r
-       i >>= 8;\r
-       return (int)i;\r
-}\r
-\r
-void DISK::start_huff()\r
-{\r
-       int i, j;\r
-       for(i = 0; i < N_CHAR; i++) {\r
-               freq[i] = 1;\r
-               son[i] = i + TABLE_SIZE;\r
-               prnt[i + TABLE_SIZE] = i;\r
-       }\r
-       i = 0; j = N_CHAR;\r
-       while(j <= ROOT_POSITION) {\r
-               freq[j] = freq[i] + freq[i + 1];\r
-               son[j] = i;\r
-               prnt[i] = prnt[i + 1] = j;\r
-               i += 2; j++;\r
-       }\r
-       freq[TABLE_SIZE] = 0xffff;\r
-       prnt[ROOT_POSITION] = 0;\r
-}\r
-\r
-void DISK::reconst()\r
-{\r
-       short i, j = 0, k;\r
-       uint16 f, l;\r
-       for(i = 0; i < TABLE_SIZE; i++) {\r
-               if(son[i] >= TABLE_SIZE) {\r
-                       freq[j] = (freq[i] + 1) / 2;\r
-                       son[j] = son[i];\r
-                       j++;\r
-               }\r
-       }\r
-       for(i = 0, j = N_CHAR; j < TABLE_SIZE; i += 2, j++) {\r
-               k = i + 1;\r
-               f = freq[j] = freq[i] + freq[k];\r
-               for(k = j - 1; f < freq[k]; k--);\r
-               k++;\r
-               l = (j - k) * 2;\r
-               memmove(&freq[k + 1], &freq[k], l);\r
-               freq[k] = f;\r
-               memmove(&son[k + 1], &son[k], l);\r
-               son[k] = i;\r
-       }\r
-       for(i = 0; i < TABLE_SIZE; i++) {\r
-               if((k = son[i]) >= TABLE_SIZE) {\r
-                       prnt[k] = i;\r
-               } else {\r
-                       prnt[k] = prnt[k + 1] = i;\r
-               }\r
-       }\r
-}\r
-\r
-void DISK::update(int c)\r
-{\r
-       int i, j, k, l;\r
-       if(freq[ROOT_POSITION] == MAX_FREQ) {\r
-               reconst();\r
-       }\r
-       c = prnt[c + TABLE_SIZE];\r
-       do {\r
-               k = ++freq[c];\r
-               if(k > freq[l = c + 1]) {\r
-                       while(k > freq[++l]);\r
-                       l--;\r
-                       freq[c] = freq[l];\r
-                       freq[l] = k;\r
-                       i = son[c];\r
-                       prnt[i] = l;\r
-                       if(i < TABLE_SIZE) {\r
-                               prnt[i + 1] = l;\r
-                       }\r
-                       j = son[l];\r
-                       son[l] = i;\r
-                       prnt[j] = c;\r
-                       if(j < TABLE_SIZE) {\r
-                               prnt[j + 1] = c;\r
-                       }\r
-                       son[c] = j;\r
-                       c = l;\r
-               }\r
-       }\r
-       while((c = prnt[c]) != 0);\r
-}\r
-\r
-short DISK::decode_char()\r
-{\r
-       int ret;\r
-       uint16 c = son[ROOT_POSITION];\r
-       while(c < TABLE_SIZE) {\r
-               if((ret = get_bit()) < 0) {\r
-                       return -1;\r
-               }\r
-               c += (unsigned)ret;\r
-               c = son[c];\r
-       }\r
-       c -= TABLE_SIZE;\r
-       update(c);\r
-       return c;\r
-}\r
-\r
-short DISK::decode_position()\r
-{\r
-       short bit;\r
-       uint16 i, j, c;\r
-       if((bit = get_byte()) < 0) {\r
-               return -1;\r
-       }\r
-       i = (uint16)bit;\r
-       c = (uint16)d_code[i] << 6;\r
-       j = d_len[i] - 2;\r
-       while(j--) {\r
-               if((bit = get_bit()) < 0) {\r
-                        return -1;\r
-               }\r
-               i = (i << 1) + bit;\r
-       }\r
-       return (c | i & 0x3f);\r
-}\r
-\r
-void DISK::init_decode()\r
-{\r
-       ibufcnt= ibufndx = bufcnt = getbuf = 0;\r
-       getlen = 0;\r
-       start_huff();\r
-       for(int i = 0; i < STRING_BUFFER_SIZE - LOOKAHEAD_BUFFER_SIZE; i++) {\r
-               text_buf[i] = ' ';\r
-       }\r
-       ptr = STRING_BUFFER_SIZE - LOOKAHEAD_BUFFER_SIZE;\r
-}\r
-\r
-int DISK::decode(uint8 *buf, int len)\r
-{\r
-       short c, pos;\r
-       int  count;\r
-       for(count = 0; count < len;) {\r
-               if(bufcnt == 0) {\r
-                       if((c = decode_char()) < 0) {\r
-                               return count;\r
-                       }\r
-                       if(c < 256) {\r
-                               *(buf++) = (uint8)c;\r
-                               text_buf[ptr++] = (uint8)c;\r
-                               ptr &= (STRING_BUFFER_SIZE - 1);\r
-                               count++;\r
-                       } else {\r
-                               if((pos = decode_position()) < 0) {\r
-                                       return count;\r
-                               }\r
-                               bufpos = (ptr - pos - 1) & (STRING_BUFFER_SIZE - 1);\r
-                               bufcnt = c - 255 + THRESHOLD;\r
-                               bufndx = 0;\r
-                       }\r
-               } else {\r
-                       while(bufndx < bufcnt && count < len) {\r
-                               c = text_buf[(bufpos + bufndx) & (STRING_BUFFER_SIZE - 1)];\r
-                               *(buf++) = (uint8)c;\r
-                               bufndx++;\r
-                               text_buf[ptr++] = (uint8)c;\r
-                               ptr &= (STRING_BUFFER_SIZE - 1);\r
-                               count++;\r
-                       }\r
-                       if(bufndx >= bufcnt) {\r
-                               bufndx = bufcnt = 0;\r
-                       }\r
-               }\r
-       }\r
-       return count;\r
-}\r
-\r
-// imagedisk image decoder\r
-\r
-bool DISK::imagedisk_to_d88()\r
-{\r
-       imd_trk_t trk;\r
-       d88_hdr_t d88_hdr;\r
-       d88_sct_t d88_sct;\r
-       \r
-       // skip comment\r
-       fi->Fseek(0, FILEIO_SEEK_SET);\r
-       int tmp;\r
-       while((tmp = fi->Fgetc()) != 0x1a) {\r
-               if(tmp == EOF) {\r
-                       return false;\r
-               }\r
-       }\r
-       \r
-       // create d88 image\r
-       file_size.d = 0;\r
-       \r
-       // create d88 header\r
-       memset(&d88_hdr, 0, sizeof(d88_hdr_t));\r
-       _strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "IMAGEDISK");\r
-       d88_hdr.protect = 0; // non-protected\r
-       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));\r
-       \r
-       // create tracks\r
-       int trkptr = sizeof(d88_hdr_t);\r
-       int trkcnt = 0, mode;\r
-       \r
-       for(int t = 0; t < 164; t++) {\r
-               // check end of file\r
-               if(fi->Fread(&trk, sizeof(imd_trk_t), 1) != 1) {\r
-                       break;\r
-               }\r
-               trkcnt = t;\r
-               \r
-               // check track header\r
-               if(t == 0) {\r
-                       mode = trk.mode % 3; // 0=500kbps, 1=300kbps, 2=250kbps\r
-               }\r
-               if(!trk.nsec) {\r
-                       continue;\r
-               }\r
-               d88_hdr.trkptr[t] = trkptr;\r
-               \r
-               // setup sector id\r
-               uint8 c[64], h[64], r[64];\r
-               fi->Fread(r, trk.nsec, 1);\r
-               if(trk.head & 0x80) {\r
-                       fi->Fread(c, trk.nsec, 1);\r
-               } else {\r
-                       memset(c, trk.cyl, sizeof(c));\r
-               }\r
-               if(trk.head & 0x40) {\r
-                       fi->Fread(h, trk.nsec, 1);\r
-               } else {\r
-                       memset(h, trk.head & 1, sizeof(h));\r
-               }\r
-               \r
-               // read sectors in this track\r
-               for(int i = 0; i < trk.nsec; i++) {\r
-                       // create d88 sector header\r
-                       int sectype = fi->Fgetc();\r
-                       if(sectype > 8) {\r
-                               return false;\r
-                       }\r
-                       memset(&d88_sct, 0, sizeof(d88_sct_t));\r
-                       d88_sct.c = c[i];\r
-                       d88_sct.h = h[i];\r
-                       d88_sct.r = r[i];\r
-                       d88_sct.n = trk.size;\r
-                       d88_sct.nsec = trk.nsec;\r
-                       d88_sct.dens = (trk.mode < 3) ? 0x40 : 0;\r
-                       d88_sct.del = (sectype == 3 || sectype == 4 || sectype == 7 || sectype == 8) ? 0x10 : 0;\r
-                       d88_sct.stat = (sectype == 5 || sectype == 6 || sectype == 7 || sectype == 8) ? 0xb0 : d88_sct.del;\r
-                       d88_sct.size = secsize[trk.size & 7];\r
-                       \r
-                       // create sector image\r
-                       uint8 dst[8192];\r
-                       if(sectype == 1 || sectype == 3 || sectype == 5 || sectype == 7) {\r
-                               // uncompressed\r
-                               fi->Fread(dst, d88_sct.size, 1);\r
-                       } else if(sectype == 2 || sectype == 4 || sectype == 6 || sectype == 8) {\r
-                               // compressed\r
-                               int tmp = fi->Fgetc();\r
-                               memset(dst, tmp, d88_sct.size);\r
-                       } else {\r
-                               d88_sct.size = 0;\r
-                       }\r
-                       \r
-                       // copy to d88\r
-                       COPYBUFFER(&d88_sct, sizeof(d88_sct_t));\r
-                       COPYBUFFER(dst, d88_sct.size);\r
-                       trkptr += sizeof(d88_sct_t) + d88_sct.size;\r
-               }\r
-       }\r
-       d88_hdr.type = (mode == 0) ? MEDIA_TYPE_2HD : ((trkcnt >> 1) > 60) ? MEDIA_TYPE_2DD : MEDIA_TYPE_2D;\r
-       d88_hdr.size = trkptr;\r
-       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));\r
-       return true;\r
-}\r
-\r
-// cpdread image decoder (from MESS formats/dsk_dsk.c)\r
-\r
-bool DISK::cpdread_to_d88(int extended)\r
-{\r
-       d88_hdr_t d88_hdr;\r
-       d88_sct_t d88_sct;\r
-       int total = 0;\r
-       \r
-       // get cylinder number and side number\r
-       memcpy(tmp_buffer, buffer, file_size.d);\r
-       int ncyl = tmp_buffer[0x30];\r
-       int nside = tmp_buffer[0x31];\r
-       \r
-       // create d88 image\r
-       file_size.d = 0;\r
-       \r
-       // create d88 header\r
-       memset(&d88_hdr, 0, sizeof(d88_hdr_t));\r
-       _strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "CPDRead");\r
-       d88_hdr.protect = 0; // non-protected\r
-       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));\r
-       \r
-       // create tracks\r
-       int trkofs = 0x100, trkofs_ptr = 0x34;\r
-       int trkptr = sizeof(d88_hdr_t);\r
-       \r
-       for(int c = 0; c < ncyl; c++) {\r
-               for(int h = 0; h < nside; h++) {\r
-                       // read sectors in this track\r
-                       uint8 *track_info = tmp_buffer + trkofs;\r
-                       int cyl = track_info[0x10];\r
-                       int side = track_info[0x11];\r
-                       int nsec = track_info[0x15];\r
-                       int size = 1 << (track_info[0x14] + 7); // standard\r
-                       int sctofs = trkofs + 0x100;\r
-                       \r
-                       if(nside == 1) {\r
-                               // double side\r
-                               d88_hdr.trkptr[2 * cyl] = d88_hdr.trkptr[2 * cyl + 1] = trkptr;\r
-                       } else {\r
-                               d88_hdr.trkptr[2 * cyl + side] = trkptr;\r
-                       }\r
-                       for(int s = 0; s < nsec; s++) {\r
-                               // get sector size\r
-                               uint8 *sector_info = tmp_buffer + trkofs + 0x18 + s * 8;\r
-                               if(extended) {\r
-                                       size = sector_info[6] + sector_info[7] * 256;\r
-                               }\r
-                               \r
-                               // create d88 sector header\r
-                               memset(&d88_sct, 0, sizeof(d88_sct_t));\r
-                               d88_sct.c = sector_info[0];\r
-                               d88_sct.h = sector_info[1];\r
-                               d88_sct.r = sector_info[2];\r
-                               d88_sct.n = sector_info[3];\r
-                               d88_sct.nsec = nsec;\r
-                               d88_sct.dens = 0;\r
-                               d88_sct.del = (sector_info[5] == 0xb2) ? 0x10 : 0;\r
-                               d88_sct.stat = (sector_info[5] == 0xb5) ? 0xb0 : d88_sct.del;\r
-                               d88_sct.size = size;\r
-                               \r
-                               // copy to d88\r
-                               COPYBUFFER(&d88_sct, sizeof(d88_sct_t));\r
-                               COPYBUFFER(tmp_buffer + sctofs, size);\r
-                               trkptr += sizeof(d88_sct_t) + size;\r
-                               sctofs += size;\r
-                               total += size;\r
-                       }\r
-                       \r
-                       if(extended) {\r
-                               trkofs += tmp_buffer[trkofs_ptr++] * 256;\r
-                       } else {\r
-                               trkofs += tmp_buffer[0x32] + tmp_buffer[0x33] * 256;\r
-                       }\r
-               }\r
-       }\r
-       d88_hdr.type = (total < (368640 + 655360) / 2) ? MEDIA_TYPE_2D : (total < (737280 + 1228800) / 2) ? MEDIA_TYPE_2DD : MEDIA_TYPE_2HD;\r
-       d88_hdr.size = trkptr;\r
-       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));\r
-       return true;\r
-}\r
-\r
-// standard image decoder\r
-\r
-bool DISK::standard_to_d88(int type, int ncyl, int nside, int nsec, int size)\r
-{\r
-       d88_hdr_t d88_hdr;\r
-       d88_sct_t d88_sct;\r
-       int n = 0, t = 0;\r
-       \r
-       file_size.d = 0;\r
-       \r
-       // create d88 header\r
-       memset(&d88_hdr, 0, sizeof(d88_hdr_t));\r
-       _strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "STANDARD");\r
-       d88_hdr.protect = 0; // non-protected\r
-       d88_hdr.type = (type == MEDIA_TYPE_144) ? MEDIA_TYPE_2HD : type;\r
-       media_type = type;\r
-       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));\r
-       \r
-       // sector length\r
-       for(int i = 0; i < 8; i++) {\r
-               if(size == (128 << i)) {\r
-                       n = i;\r
-                       break;\r
-               }\r
-       }\r
-       \r
-       // create tracks\r
-       int trkptr = sizeof(d88_hdr_t);\r
-       for(int c = 0; c < ncyl; c++) {\r
-               for(int h = 0; h < nside; h++) {\r
-                       d88_hdr.trkptr[t++] = trkptr;\r
-                       if(nside == 1) {\r
-                               // double side\r
-                               d88_hdr.trkptr[t++] = trkptr;\r
-                       }\r
-                       \r
-                       // read sectors in this track\r
-                       for(int s = 0; s < nsec; s++) {\r
-                               // create d88 sector header\r
-                               memset(&d88_sct, 0, sizeof(d88_sct_t));\r
-                               d88_sct.c = c;\r
-                               d88_sct.h = h;\r
-                               d88_sct.r = s + 1;\r
-                               d88_sct.n = n;\r
-                               d88_sct.nsec = nsec;\r
-                               d88_sct.dens = 0;\r
-                               d88_sct.del = 0;\r
-                               d88_sct.stat = 0;\r
-                               d88_sct.size = size;\r
-                               \r
-                               // create sector image\r
-                               uint8 dst[16384];\r
-                               memset(dst, 0xe5, sizeof(dst));\r
-                               fi->Fread(dst, size, 1);\r
-                               \r
-                               // copy to d88\r
-                               COPYBUFFER(&d88_sct, sizeof(d88_sct_t));\r
-                               COPYBUFFER(dst, size);\r
-                               trkptr += sizeof(d88_sct_t) + size;\r
-                       }\r
-               }\r
-       }\r
-       d88_hdr.size = trkptr;\r
-       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));\r
-       return true;\r
-}\r
-\r
-#define STATE_VERSION  4\r
-\r
-void DISK::save_state(FILEIO* state_fio)\r
-{\r
-       state_fio->FputUint32(STATE_VERSION);\r
-       \r
-       state_fio->Fwrite(buffer, sizeof(buffer), 1);\r
-       state_fio->Fwrite(orig_path, sizeof(orig_path), 1);\r
-       state_fio->Fwrite(dest_path, sizeof(dest_path), 1);\r
-       state_fio->FputUint32(file_size.d);\r
-       state_fio->FputInt32(file_bank);\r
-       state_fio->FputUint32(crc32);\r
-       state_fio->Fwrite(fdi_header, sizeof(fdi_header), 1);\r
-       state_fio->FputBool(inserted);\r
-       state_fio->FputBool(ejected);\r
-       state_fio->FputBool(write_protected);\r
-       state_fio->FputBool(changed);\r
-       state_fio->FputUint8(media_type);\r
-       state_fio->FputBool(is_standard_image);\r
-       state_fio->FputBool(is_fdi_image);\r
-       state_fio->FputInt32(is_special_disk);\r
-       state_fio->Fwrite(track, sizeof(track), 1);\r
-       state_fio->FputInt32(sector_num.sd);\r
-       state_fio->FputBool(invalid_format);\r
-       state_fio->FputBool(no_skew);\r
-       state_fio->Fwrite(sync_position, sizeof(sync_position), 1);\r
-       state_fio->Fwrite(id_position, sizeof(id_position), 1);\r
-       state_fio->Fwrite(data_position, sizeof(data_position), 1);\r
-       state_fio->FputInt32(sector ? (int)(sector - buffer) : -1);\r
-       state_fio->FputInt32(sector_size.sd);\r
-       state_fio->Fwrite(id, sizeof(id), 1);\r
-       state_fio->FputUint8(density);\r
-       state_fio->FputBool(deleted);\r
-       state_fio->FputBool(crc_error);\r
-       state_fio->FputUint8(drive_type);\r
-       state_fio->FputInt32(drive_rpm);\r
-       state_fio->FputBool(drive_mfm);\r
-}\r
-\r
-bool DISK::load_state(FILEIO* state_fio)\r
-{\r
-       if(state_fio->FgetUint32() != STATE_VERSION) {\r
-               return false;\r
-       }\r
-       state_fio->Fread(buffer, sizeof(buffer), 1);\r
-       state_fio->Fread(orig_path, sizeof(orig_path), 1);\r
-       state_fio->Fread(dest_path, sizeof(dest_path), 1);\r
-       file_size.d = state_fio->FgetUint32();\r
-       file_bank = state_fio->FgetInt32();\r
-       crc32 = state_fio->FgetUint32();\r
-       state_fio->Fread(fdi_header, sizeof(fdi_header), 1);\r
-       inserted = state_fio->FgetBool();\r
-       ejected = state_fio->FgetBool();\r
-       write_protected = state_fio->FgetBool();\r
-       changed = state_fio->FgetBool();\r
-       media_type = state_fio->FgetUint8();\r
-       is_standard_image = state_fio->FgetBool();\r
-       is_fdi_image = state_fio->FgetBool();\r
-       is_special_disk = state_fio->FgetInt32();\r
-       state_fio->Fread(track, sizeof(track), 1);\r
-       sector_num.sd = state_fio->FgetInt32();\r
-       invalid_format = state_fio->FgetBool();\r
-       no_skew = state_fio->FgetBool();\r
-       state_fio->Fread(sync_position, sizeof(sync_position), 1);\r
-       state_fio->Fread(id_position, sizeof(id_position), 1);\r
-       state_fio->Fread(data_position, sizeof(data_position), 1);\r
-       int offset = state_fio->FgetInt32();\r
-       sector = (offset != -1) ? buffer + offset : NULL;\r
-       sector_size.sd = state_fio->FgetInt32();\r
-       state_fio->Fread(id, sizeof(id), 1);\r
-       density = state_fio->FgetUint8();\r
-       deleted = state_fio->FgetBool();\r
-       crc_error = state_fio->FgetBool();\r
-       drive_type = state_fio->FgetUint8();\r
-       drive_rpm = state_fio->FgetInt32();\r
-       drive_mfm = state_fio->FgetBool();\r
-       return true;\r
-}\r
-\r
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2006.09.16-
+
+       [ d88 handler ]
+*/
+
+#ifndef _ANY2D88
+#include "../emu.h"
+#endif
+
+#include "disk.h"
+#include "../fileio.h"
+
+#ifndef _ANY2D88
+#define local_path(x) create_local_path(x)
+#else
+#define local_path(x) (x)
+#endif
+#define FM     false
+#define MFM    true
+
+// crc table
+static const uint16_t crc_table[256] = {
+       0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+       0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+       0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+       0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+       0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+       0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+       0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+       0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+       0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+       0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+       0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+       0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+       0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+       0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+       0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+       0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+static const int secsize[8] = {
+       128, 256, 512, 1024, 2048, 4096, 8192, 16384
+};
+
+static uint8_t tmp_buffer[DISK_BUFFER_SIZE];
+
+static const fd_format_t fd_formats_base[] = {
+       { MEDIA_TYPE_2D,  35, 1, 16,  128, FM  },       // 1S   70KB
+       { MEDIA_TYPE_2D,  35, 2, 16,  128, FM  },       // 2S   140KB
+       { MEDIA_TYPE_2DD, 77, 1, 26,  128, FM  },       // 1S   250KB
+       { MEDIA_TYPE_2D,  40, 1,  8,  512, MFM },       // 1D   160KB
+       { MEDIA_TYPE_2D,  40, 1,  9,  512, MFM },       // 1D   180KB
+       { MEDIA_TYPE_2D,  40, 1, 10,  512, MFM },       // 1D   200KB
+//#if defined(SUPPORT_MEDIA_TYPE_1DD)
+//     { MEDIA_TYPE_2DD, 70, 1,  8,  512, MFM },       // 1DD  280KB
+//     { MEDIA_TYPE_2DD, 70, 1,  9,  512, MFM },       // 1DD  315KB
+//     { MEDIA_TYPE_2DD, 70, 1, 10,  512, MFM },       // 1DD  350KB
+//     { MEDIA_TYPE_2DD, 80, 1,  8,  512, MFM },       // 1DD  320KB
+//     { MEDIA_TYPE_2DD, 80, 1,  9,  512, MFM },       // 1DD  360KB
+//     { MEDIA_TYPE_2DD, 80, 1, 10,  512, MFM },       // 1DD  400KB
+//#else
+       { MEDIA_TYPE_2D,  35, 2,  8,  512, MFM },       // 2D   280KB
+       { MEDIA_TYPE_2D,  35, 2,  9,  512, MFM },       // 2D   315KB
+       { MEDIA_TYPE_2D,  35, 2, 10,  512, MFM },       // 2D   350KB
+       { MEDIA_TYPE_2D,  40, 2,  8,  512, MFM },       // 2D   320KB
+       { MEDIA_TYPE_2D,  40, 2,  9,  512, MFM },       // 2D   360KB
+       { MEDIA_TYPE_2D,  40, 2, 10,  512, MFM },       // 2D   400KB
+//#endif
+       { MEDIA_TYPE_2DD, 80, 2,  8,  512, MFM },       // 2DD  640KB
+       { MEDIA_TYPE_2DD, 80, 2,  9,  512, MFM },       // 2DD  720KB
+       { MEDIA_TYPE_2DD, 81, 2,  9,  512, MFM },       // 2DD  729KB, ASCII MSX
+       { MEDIA_TYPE_2DD, 80, 2, 10,  512, MFM },       // 2DD  800KB
+       { MEDIA_TYPE_2HD, 77, 2, 26,  256, MFM },       // 2HD  1001KB, MITSUBISHI/IBM
+       { MEDIA_TYPE_2HD, 80, 2, 15,  512, MFM },       // 2HC  1200KB, TOSHIBA/IBM
+       { MEDIA_TYPE_2HD, 77, 2,  8, 1024, MFM },       // 2HD  1232KB, NEC
+       { MEDIA_TYPE_144, 80, 2, 18,  512, MFM },       // 2HD  1440KB
+       { MEDIA_TYPE_144, 80, 2, 21,  512, MFM },       // 2HD  1680KB
+       { MEDIA_TYPE_144, 82, 2, 21,  512, MFM },       // 2HD  1722KB
+       { MEDIA_TYPE_144, 80, 2, 36,  512, MFM },       // 2ED  2880KB
+       { -1, 0, 0, 0, 0 },
+};
+
+#define IS_VALID_TRACK(offset) ((offset) >= 0x20 && (offset) < sizeof(buffer))
+
+void DISK::setup_fd_formats(void)
+{
+       fd_format_t nt;
+       bool checked = false;
+       memset(fd_formats, 0, sizeof(fd_formats));
+#if !defined(_ANY2D88)
+       osd = emu->get_osd();
+       if(osd->check_feature(_T("_SC3000"))) {
+               checked = true;
+               type_sc3000 = true;
+               nt = { MEDIA_TYPE_2D,  40, 1, 16,  256, MFM };  // 1D   160KB
+       } else if(osd->check_feature(_T("SMC70")) || osd->check_feature(_T("_SMC777"))) {
+               checked = true;
+               type_smc70 = true;
+               nt = { MEDIA_TYPE_2DD, 70, 1, 16,  256, MFM };  // 1DD  280KB
+       } else if (osd->check_feature(_T("_X1")) || osd->check_feature(_T("_X1TWIN"))) {
+               checked = true;
+               type_x1 = true;
+               nt = { MEDIA_TYPE_2D,  40, 2, 16,  256, MFM };  // 2D   320KB
+       } else if(osd->check_feature(_T("_X1TURBO")) || osd->check_feature(_T("_X1TURBOZ"))) {
+               checked = true;
+               type_x1 = true;
+               type_x1turbo = true;
+               nt = { MEDIA_TYPE_2D,  40, 2, 16,  256, MFM };  // 2D   320KB
+       } else if(osd->check_feature(_T("_M5"))) {
+               checked = true;
+               type_m5 = true;
+               nt = { MEDIA_TYPE_2D,  40, 2, 18,  256, MFM };  // 2D   360KB
+       } else if(osd->check_feature(_T("_MZ80B")) || osd->check_feature(_T("_MZ2000")) ||
+                         osd->check_feature(_T("_MZ2200")) || osd->check_feature(_T("_MZ2500"))) {
+               checked = true;
+               type_mz80b = true;
+               nt = { MEDIA_TYPE_2DD, 80, 2, 16,  256, MFM };  // 2DD  640KB
+       } else if (osd->check_feature(_T("_YIS"))) {
+               checked = true;
+               type_yis = true;
+               nt = { MEDIA_TYPE_2DD,  80, 1, 16,  256, MFM }; // 1DD  320KB
+       }
+       if(osd->check_feature(_T("_FM7")) || osd->check_feature(_T("_FM8"))) {
+               checked = false;
+               type_fm7 = true;
+       } else if(osd->check_feature(_T("_FM77_VARIANTS"))) {
+               checked = false;
+               type_fm7 = true;
+               type_fm77 = true;
+       } else if(osd->check_feature(_T("_FM77AV_VARIANTS"))) {
+               checked = false;
+               type_fm7 = true;
+               type_fm77 = true;
+               type_fm77av = true;
+               if(osd->check_feature(_T("_FM77AV40")) || osd->check_feature(_T("_FM77AV40EX")) ||
+                  osd->check_feature(_T("_FM77AV40SX")) || osd->check_feature(_T("_FM77AV20")) ||
+                  osd->check_feature(_T("_FM77AV20EX"))) {
+                       type_fm77av_2dd = true;
+               }
+       }
+       if(osd->check_feature(_T("SUPPORT_MEDIA_TYPE_1DD"))) {
+               type_1dd = true;
+       }
+       if(osd->check_feature(_T("_ANY2D88"))) {
+               type_any2d88 = true;
+       }
+#else // _ANY2D88
+       type_any2d88 = true;
+#endif 
+       if(checked) {
+               fd_formats[0] = nt;
+               memcpy(&fd_formats[1], fd_formats_base, sizeof(fd_formats_base));
+       } else {
+               memcpy(&fd_formats[0], fd_formats_base, sizeof(fd_formats_base));
+       }               
+}
+
+void DISK::open(const _TCHAR* file_path, int bank)
+{
+       // check current disk image
+       if(inserted) {
+               if(_tcsicmp(orig_path, file_path) == 0 && file_bank == bank) {
+                       return;
+               }
+               close();
+       }
+       if(bank < 0) {
+               return;
+       }
+       memset(buffer, 0, sizeof(buffer));
+       file_bank = 0;
+       write_protected = false;
+       media_type = MEDIA_TYPE_UNK;
+       is_special_disk = 0;
+       is_solid_image = is_fdi_image = is_1dd_image = false;
+       trim_required = false;
+       track_mfm = drive_mfm;
+       
+       // open disk image
+       FILEIO *fio = new FILEIO();
+       if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
+               my_tcscpy_s(orig_path, _MAX_PATH, file_path);
+               my_tcscpy_s(dest_path, _MAX_PATH, file_path);
+               
+               file_size.d = fio->FileLength();
+               fio->Fseek(0, FILEIO_SEEK_SET);
+               
+               if(check_file_extension(file_path, _T(".d88")) || check_file_extension(file_path, _T(".d77")) || check_file_extension(file_path, _T(".1dd"))) {
+                       // d88 image
+                       uint32_t offset = 0;
+                       for(int i = 0; i < bank; i++) {
+                               fio->Fseek(offset + 0x1c, SEEK_SET);
+                               offset += fio->FgetUint32_LE();
+                       }
+                       fio->Fseek(offset + 0x1c, FILEIO_SEEK_SET);
+                       file_size.d = fio->FgetUint32_LE();
+                       fio->Fseek(offset, FILEIO_SEEK_SET);
+                       fio->Fread(buffer, file_size.d, 1);
+                       file_bank = bank;
+                       if(check_file_extension(file_path, _T(".1dd"))) {
+                               is_1dd_image = true;
+                               media_type = MEDIA_TYPE_2DD;
+                       }
+                       inserted = changed = true;
+//                     trim_required = true;
+                       
+                       // fix sector number from big endian to little endian
+                       for(int trkside = 0; trkside < 164; trkside++) {
+                               pair_t offset;
+                               offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+                               
+                               if(!IS_VALID_TRACK(offset.d)) {
+                                       break;
+                               }
+                               uint8_t* t = buffer + offset.d;
+                               pair_t sector_num, data_size;
+                               sector_num.read_2bytes_le_from(t + 4);
+                               bool is_be = (sector_num.b.l == 0 && sector_num.b.h >= 4);
+                               if(is_be) {
+                                       sector_num.read_2bytes_be_from(t + 4);
+                                       sector_num.write_2bytes_le_to(t + 4);
+                               }
+                               for(int i = 0; i < sector_num.sd; i++) {
+                                       if(is_be) {
+                                               sector_num.write_2bytes_le_to(t + 4);
+                                       }
+                                       data_size.read_2bytes_le_from(t + 14);
+                                       t += data_size.sd + 0x10;
+                               }
+                       }
+               } else if(check_file_extension(file_path, _T(".td0"))) {
+                       // teledisk image
+                       try {
+                               if(teledisk_to_d88(fio)) {
+                                       inserted = changed = true;
+                                       my_stprintf_s(dest_path, _MAX_PATH, _T("%s.D88"), file_path);
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".imd"))) {
+                       // imagedisk image
+                       try {
+                               if(imagedisk_to_d88(fio)) {
+                                       inserted = changed = true;
+                                       my_stprintf_s(dest_path, _MAX_PATH, _T("%s.D88"), file_path);
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".dsk"))) {
+                       // cpdread image
+                       try {
+                               if(cpdread_to_d88(fio)) {
+                                       inserted = changed = true;
+                                       my_stprintf_s(dest_path, _MAX_PATH, _T("%s.D88"), file_path);
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".nfd"))) {
+                       // T98-NEXT nfd r0/r1 image for NEC PC-98x1 series
+                       try {
+                               if(nfdr0_to_d88(fio) || nfdr1_to_d88(fio)) {
+                                       inserted = changed = true;
+                                       my_stprintf_s(dest_path, _MAX_PATH, _T("%s.D88"), file_path);
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".fdi"))) {
+                       // Anex86 fdi image for NEC PC-98x1 series
+                       if(file_size.d == 4096 + 77 * 2 * 8 * 1024) {
+                               try {
+                                       fio->Fread(fdi_header, 4096, 1);
+                                       if(solid_to_d88(fio, MEDIA_TYPE_2HD, 77, 2, 8, 1024, true)) {
+                                               inserted = changed = is_solid_image = is_fdi_image = true;
+                                       }
+                               } catch(...) {
+                                       // failed to convert the disk image
+                               }
+                       } else if(file_size.d == 4096 + 80 * 2 * 15 * 512) {
+                               try {
+                                       fio->Fread(fdi_header, 4096, 1);
+                                       if(solid_to_d88(fio, MEDIA_TYPE_2HD, 80, 2, 15, 512, true)) {
+                                               inserted = changed = is_solid_image = is_fdi_image = true;
+                                       }
+                               } catch(...) {
+                                       // failed to convert the disk image
+                               }
+                       } else if(file_size.d == 4096 + 80 * 2 * 18 * 512) {
+                               try {
+                                       fio->Fread(fdi_header, 4096, 1);
+                                       if(solid_to_d88(fio, MEDIA_TYPE_144, 80, 2, 18, 512, true)) {
+                                               inserted = changed = is_solid_image = is_fdi_image = true;
+                                       }
+                               } catch(...) {
+                                       // failed to convert the disk image
+                               }
+                       } else if(file_size.d == 4096 + 77 * 2 * 26 * 256) {
+                               try {
+                                       fio->Fread(fdi_header, 4096, 1);
+                                       if(solid_to_d88(fio, MEDIA_TYPE_2HD, 77, 2, 26, 256, true)) {
+                                               inserted = changed = is_solid_image = is_fdi_image = true;
+                                       }
+                               } catch(...) {
+                                       // failed to convert the disk image
+                               }
+                       } else if(file_size.d == 4096 + 80 * 2 * 9 * 512) {
+                               try {
+                                       fio->Fread(fdi_header, 4096, 1);
+                                       if(solid_to_d88(fio, MEDIA_TYPE_2DD, 80, 2, 9, 512, true)) {
+                                               inserted = changed = is_solid_image = is_fdi_image = true;
+                                       }
+                               } catch(...) {
+                                       // failed to convert the disk image
+                               }
+                       } else if(file_size.d == 4096 + 80 * 2 * 8 * 512) {
+                               try {
+                                       fio->Fread(fdi_header, 4096, 1);
+                                       if(solid_to_d88(fio, MEDIA_TYPE_2DD, 80, 2, 8, 512, true)) {
+                                               inserted = changed = is_solid_image = is_fdi_image = true;
+                                       }
+                               } catch(...) {
+                                       // failed to convert the disk image
+                               }
+                       }
+               } else if(check_file_extension(file_path, _T(".hdm")) && file_size.d == 77 * 2 * 8 * 1024) {
+                       // BKDSK hdm image for NEC PC-98x1 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2HD, 77, 2, 8, 1024, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".hd5")) && file_size.d == 80 * 2 * 15 * 512) {
+                       // BKDSK hd5 image for NEC PC-98x1 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2HD, 80, 2, 15, 512, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".hd4")) && file_size.d == 80 * 2 * 18 * 512) {
+                       // BKDSK hd4 image for NEC PC-98x1 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_144, 80, 2, 18, 512, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".hdb")) && file_size.d == 77 * 2 * 26 * 256) {
+                       // BKDSK hdb image for NEC PC-98x1 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2HD, 77, 2, 26, 256, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".dd9")) && file_size.d == 80 * 2 * 9 * 512) {
+                       // BKDSK dd9 image for NEC PC-98x1 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2DD, 80, 2, 9, 512, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".dd6")) && file_size.d == 80 * 2 * 8 * 512) {
+                       // BKDSK dd6 image for NEC PC-98x1 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2DD, 80, 2, 8, 512, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".xdf")) && file_size.d == 77 * 2 * 8 * 1024) {
+                       // EX68 xdf image for SHARP X680x0 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2HD, 77, 2, 8, 1024, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".2d"))  && file_size.d == 40 * 2 * 16 * 256) {
+                       // 2d image for SHARP X1 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2D, 40, 2, 16, 256, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".sf7")) && file_size.d == 40 * 1 * 16 * 256) {
+                       // sf7 image for SEGA SC-3000 + SF-7000
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2D, 40, 1, 16, 256, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               } else if(check_file_extension(file_path, _T(".img"))  && file_size.d == 70 * 1 * 16 * 256) {
+                       // img image for SONY SMC-70/777 series
+                       try {
+                               if(solid_to_d88(fio, MEDIA_TYPE_2DD, 70, 1, 16, 256, true)) {
+                                       inserted = changed = is_solid_image = true;
+                               }
+                       } catch(...) {
+                               // failed to convert the disk image
+                       }
+               }
+               if(!inserted) {
+                       // check solid image file format
+                       for(int i = 0;; i++) {
+                               const fd_format_t *p = &fd_formats[i];
+                               if(p->type == -1) {
+                                       break;
+                               }
+                               if(file_size.d == (uint32_t)(p->ncyl * p->nside * p->nsec * p->size)) {
+                                       fio->Fseek(0, FILEIO_SEEK_SET);
+                                       int type = p->type;
+                                       int ncyl = p->ncyl;
+                                       int nside = p->nside;
+                                       int nsec = p->nsec;
+                                       int size = p->size;
+//#if defined(SUPPORT_MEDIA_TYPE_1DD)
+                                               if(type_1dd) {
+                                                       if(type == MEDIA_TYPE_2D && nside == 2 && p->mfm) {
+                                                               type = MEDIA_TYPE_2DD;
+                                                               nside = 1;
+                                                               ncyl *= 2;
+                                                       }
+                                               } else if(type_any2d88) {
+//#elif defined(_ANY2D88)
+                                                       if(open_as_1dd && type == MEDIA_TYPE_2D && nside == 2 && p->mfm) {
+                                                               type = MEDIA_TYPE_2DD;
+                                                               nside = 1;
+                                                               ncyl *= 2;
+                                                       }
+                                                       if(open_as_256 && (size == 512 || size == 1024)) {
+                                                               nsec *= size / 256;
+                                                               size = 256;
+                                                       }
+                                               }
+//#endif
+                                       try {
+//                                             if(solid_to_d88(fio, p->type, p->ncyl, p->nside, p->nsec, p->size, p->mfm)) {
+                                               if(solid_to_d88(fio, type, ncyl, nside, nsec, size, p->mfm)) {
+                                                       inserted = changed = is_solid_image = true;
+                                               }
+                                       } catch(...) {
+                                               // failed to convert the disk image
+                                       }
+                                       if(inserted) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               if(fio->IsOpened()) {
+                       fio->Fclose();
+               }
+       }
+       delete fio;
+       
+       // check loaded image
+       if(inserted) {
+               // check media type
+               if(media_type == MEDIA_TYPE_UNK) {
+                       if((media_type = buffer[0x1b]) == MEDIA_TYPE_2HD) {
+                               // check 1.2MB or 1.44MB
+                               for(int trkside = 0; trkside < 164; trkside++) {
+                                       pair_t offset;
+                                       offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+                                       
+                                       if(!IS_VALID_TRACK(offset.d)) {
+                                               continue;
+                                       }
+                                       // track found
+                                       uint8_t *t = buffer + offset.d;
+                                       pair_t sector_num, data_size;
+                                       sector_num.read_2bytes_le_from(t + 4);
+                                       data_size.read_2bytes_le_from(t + 14);
+                                       
+                                       if(sector_num.sd >= 18 && data_size.sd == 512) {
+                                               media_type = MEDIA_TYPE_144;
+                                       }
+                                       break;
+                               }
+                       }
+               }
+               
+               // check two side
+               int valid_side = 0;
+               
+               for(int trk = 0; trk < 82; trk++) {
+                       for(int side = 0; side < 2; side++) {
+                               int trkside = trk * 2 + side;
+                               pair_t offset;
+                               offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+                               
+                               if(IS_VALID_TRACK(offset.d)) {
+                                       valid_side |= (1 << side);
+                               }
+                       }
+                       if(valid_side == 3) break;
+               }
+               // FIXME: unformat disk is recognized as two side
+               two_side = (valid_side != 1);
+               
+               // fix write protect flag
+               if(buffer[0x1a] != 0) {
+                       buffer[0x1a] = 0x10;
+                       write_protected = true;
+               }
+               
+               // get crc32 for midification check
+               orig_file_size = file_size.d;
+               orig_crc32 = get_crc32(buffer, file_size.d);
+               
+               // check special disk image
+//#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
+               if(type_fm7) {
+                       // FIXME: ugly patch for FM-7 Gambler Jiko Chuushin Ha, DEATH FORCE and Psy-O-Blade
+                       if(media_type == MEDIA_TYPE_2D) {
+                               // check first track
+                               pair_t offset, sector_num, data_size;
+                               offset.read_4bytes_le_from(buffer + 0x20);
+
+                               if(IS_VALID_TRACK(offset.d)) {
+                                       // check the sector (c,h,r,n) = (0,0,7,1) or (0,0,f7,2)
+                                       uint8_t* t = buffer + offset.d;
+                                       sector_num.read_2bytes_le_from(t + 4);
+                                       for(int _i = 0; _i < sector_num.sd; _i++) {
+                                               data_size.read_2bytes_le_from(t + 14);
+                                               if(data_size.sd == 0x100 && t[0] == 0 && t[1] == 0 && t[2] == 7 && t[3] == 1) {
+                                                       /* Type 1: Sec07, +$50- "1989/09/12 ... "*/
+                                                       static const uint8_t gamblerfm_1[] = {
+                                                               0x31, 0x39, 0x38, 0x39, 0x2f, 0x30, 0x39, 0x2f,
+                                                               0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                               0x28, 0x43, 0x29, 0x47, 0x41, 0x4d, 0x45, 0x41,
+                                                               0x52, 0x54, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                               0x20, 0x20, 0x20, 0x59, 0x45, 0x4c, 0x4c, 0x4f,
+                                                               0x57, 0x48, 0x4f, 0x52, 0x4e, 0x00, 0x00, 0x00,
+                                                               0x20, 0x20, 0x20, 0x4b, 0x4f, 0x55, 0x44, 0x41,
+                                                               0x4e, 0x53, 0x59, 0x41, 0x20, 0x59, 0x4f, 0x55,
+                                                               0x4e, 0x47, 0x2d, 0x4d, 0x41, 0x47, 0x41, 0x5a,
+                                                               0x49, 0x4e, 0x45, 0x00 
+                                                       };
+                                                       /* Type 2: Sec07, +$30- */
+                                                       static const uint8_t gamblerfm_2[] = {
+                                                               0xb7, 0xde, 0xad, 0xdc, 0xdd, 0xcc, 0xde, 0xd7,
+                                                               0xb1, 0x20, 0xbc, 0xde, 0xba, 0xc1, 0xad, 0xb3,
+                                                               0xbc, 0xdd, 0xca
+                                                       };
+                                                       if(memcmp((void *)(t + 0x50), gamblerfm_1, sizeof(gamblerfm_1)) == 0) {
+                                                               is_special_disk = SPECIAL_DISK_FM7_GAMBLER;
+                                                               break;
+                                                       }
+                                                       if(memcmp((void *)(t + 0x30), gamblerfm_2, sizeof(gamblerfm_2)) == 0) {
+                                                               is_special_disk = SPECIAL_DISK_FM7_GAMBLER;
+                                                               break;
+                                                       }
+                                               } else if(data_size.sd == 0x200 && t[0] == 0 && t[1] == 0 && t[2] == 0xf7 && t[3] == 2) {
+                                                       //"DEATHFORCE/77AV" + $f7*17 + $00 + $00
+                                                       static const uint8_t deathforce[] ={
+                                                               0x44, 0x45, 0x41, 0x54, 0x48, 0x46, 0x4f, 0x52,
+                                                               0x43, 0x45, 0x2f, 0x37, 0x37, 0x41, 0x56, 0xf7,
+                                                               0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+                                                               0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+                                                               0x00, 0x00
+                                                       };
+                                                       if(memcmp((void *)(t + 0x10), deathforce, sizeof(deathforce)) == 0) {
+                                                               is_special_disk = SPECIAL_DISK_FM7_DEATHFORCE;
+                                                               break;
+                                                       }
+                                               } else if(data_size.sd == 0x100 && t[0] == 0 && t[1] == 0 && t[2] == 5 && t[3] == 1) {
+                                                       //$00 * 16 + $00 + "Protected by  N & A (SUPER HACKER COMBI)
+                                                       //Can you found CHECK-ROUTINES ?Can you crack these protect ?
+                                                       //good bye !ou "
+                                                       static const uint8_t xanadu2fm_d_1[] ={
+                                                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                       };
+                                                       static const uint8_t xanadu2fm_d_2[] = {
+                                                               0x50, 0x72, 0x6F, 0x74, 0x65, 0x63, 0x74, 0x65,
+                                                               0x64, 0x20, 0x62, 0x79, 0x20, 0x20, 0x4E, 0x20,
+                                                               0x26, 0x20, 0x41, 0x20, 0x28, 0x53, 0x55, 0x50,
+                                                               0x45, 0x52, 0x20, 0x48, 0x41, 0x43, 0x4B, 0x45,
+                                                               0x52, 0x20, 0x43, 0x4F, 0x4D, 0x42, 0x49, 0x29,
+                                                               0x43, 0x61, 0x6E, 0x20, 0x79, 0x6F, 0x75, 0x20,
+                                                               0x66, 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x43, 0x48,
+                                                               0x45, 0x43, 0x4B, 0x2D, 0x52, 0x4F, 0x55, 0x54,
+                                                               0x49, 0x4E, 0x45, 0x53, 0x20, 0x3F, 0x43, 0x61,
+                                                               0x6E, 0x20, 0x79, 0x6F, 0x75, 0x20, 0x63, 0x72,
+                                                               0x61, 0x63, 0x6B, 0x20, 0x74, 0x68, 0x65, 0x73,
+                                                               0x65, 0x20, 0x70, 0x72, 0x6F, 0x74, 0x65, 0x63,
+                                                               0x74, 0x20, 0x3F, 0x67, 0x6F, 0x6F, 0x64, 0x20,
+                                                               0x62, 0x79, 0x65, 0x20, 0x21, 0x6F, 0x75, 0x20,
+                                                       };
+                                                       if(memcmp((void *)(t + 0x10 + 0x60), xanadu2fm_d_1, sizeof(xanadu2fm_d_1)) == 0) {
+                                                               if(memcmp((void *)(t + 0x10 + 0x70), xanadu2fm_d_2, sizeof(xanadu2fm_d_2)) == 0) {
+                                                                       is_special_disk = SPECIAL_DISK_FM7_XANADU2_D;
+                                                                       break;
+                                                               }
+                                                       }
+                                               } else if(data_size.sd == 0x100 && t[0] == 0 && t[1] == 0 && t[2] == 8 && t[3] == 1) {
+                                                       // Xanadu 1
+                                                       static const uint8_t xanadu1fm_d_1[] = {
+                                                               0xFF, 0x43, 0x6F, 0x6E, 0x74, 0x69, 0x6E, 0x75,
+                                                               0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x70,
+                                                               0x72, 0x6F, 0x67, 0x72, 0x61 ,0x6D, 0x2E, 0x0D,
+                                                               0xFF, 0x00, 0x34, 0x01, 0x1C ,0xAF, 0x6F, 0x8D,
+                                                               0x01, 0x3F, 0x17, 0xFA, 0xE4, 0x27, 0xFB, 0x35,
+                                                               0x81, 0x34, 0x13, 0xA6, 0x80, 0x81, 0xFF, 0x27,
+                                                               0x04, 0x8D, 0x04, 0x20, 0xF6, 0x35, 0x93, 0x34,
+                                                               0x15, 0xA7, 0x8D, 0x00, 0xB6, 0xC6, 0x05, 0x30,
+                                                               0x8D, 0x00, 0xAC, 0x17, 0x00, 0xB6, 0x17, 0x00,
+                                                               0xD6, 0x35, 0x95, 0x00, 0x00, 0x3F, 0x59, 0x41,
+                                                               0x4D, 0x41, 0x55, 0x43, 0x48, 0x49, 0x91, 0xD3,
+                                                       };
+                                                       static const uint8_t xanadu1fm_d_2[] = {
+                                                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                               0x00, 0x00, 0x00, 0x00, 0x3F, 0x59, 0x41, 0x4D,
+                                                               0x41, 0x55, 0x43, 0x48, 0x49, 0x93, 0xD3, 0x8F,
+                                                               0x90, 0x8E, 
+                                                       };
+                                                       if(memcmp((void *)(t + 0x10 + 0), xanadu1fm_d_1, sizeof(xanadu1fm_d_1)) == 0) {
+                                                               if(memcmp((void *)(t + 0x10 + 0xb0), xanadu1fm_d_2, sizeof(xanadu1fm_d_2)) == 0) {
+                                                                       is_special_disk = SPECIAL_DISK_FM7_XANADU2_D; // Same issue as Xanadu2.
+                                                                       break;
+                                                               }
+                                                       }
+                                               } else if(data_size.sd == 0x100 && t[0] == 0 && t[1] == 0 && t[2] == 1 && t[3] == 1) {
+                                                       //$03 + $2D + "PSY-O-BLADE   Copyright 1988 by T&E SOFT Inc." + $B6 + $FD + $05
+                                                       static const uint8_t psyoblade_ipl1[] ={
+                                                               0x03, 0x2d, 0x50, 0x53, 0x59, 0xa5, 0x4f, 0xa5,
+                                                               0x42, 0x4c, 0x41, 0x44, 0x45, 0x20, 0x20, 0x20,
+                                                               0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68,
+                                                               0x74, 0x20, 0x31, 0x39, 0x38, 0x38, 0x20, 0x62,
+                                                               0x79, 0x20, 0x54, 0x26, 0x45, 0x20, 0x53, 0x4f,
+                                                               0x46, 0x54, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0xb6,
+                                                               0xfd, 0x05
+                                                       };
+                                                       //IPL Signature1
+                                                       static const uint8_t psyoblade_disk_1[] ={
+                                                               0xc3, 0x00, 0x01, 0x00, 0x1a, 0x50, 0x86, 0xff,
+                                                               0xb7, 0xfd, 0x10, 0xb7, 0xfd, 0x0f, 0x30, 0x8c,
+                                                               0x0e, 0x8d, 0x35, 0x30, 0x8c, 0x14, 0x8d, 0x30,
+                                                               0x30, 0x8c, 0x14, 0x8d, 0x2b, 0x20, 0xfe, 0x0a,
+                                                       };
+                                                       //$00 + $00 + $03 + $14 + "PSY-O-BLADE  DISK" + $B6 + $FD + $05
+                                                       static const uint8_t psyoblade_disk_2[] ={
+                                                               0x00, 0x00, 0x03, 0x14, 0x50, 0x53, 0x59, 0xa5,
+                                                               0x4f, 0xa5, 0x42, 0x4c, 0x41, 0x44, 0x45, 0x20,
+                                                               0x20, 0x20, 0x44, 0x49, 0x53, 0x4B, 0x20
+                                                       };
+                                                       //RIGLAS/FM: $3E - $7D 
+                                                       static const uint8_t riglas_fm[] = {
+                                                               0x0d, 0x56, /* $3e-$3f */
+                                                               0x44, 0x53, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, /* $40-$47 */
+                                                               0x43, 0x4d, 0x44, 0x00, 0x00, 0x02, 0x1b, 0x02, /* $48-$4f */
+                                                               0x1e, 0x00, 0x04, 0x00, 0x00, 0x03, 0x0d, 0x56, /* $50-$57 */
+                                                               0x43, 0x4f, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00, /* $58-$5f */
+                                                               0x43, 0x4d, 0x44, 0x00, 0x00, 0x02, 0x1f, 0x03, /* $60-$67 */
+                                                               0x03, 0x00, 0x05, 0x00, 0x00, 0x03, 0x0d, 0x56, /* $68-$6f */
+                                                               0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x00, 0x00, /* $70-$77 */
+                                                               0x43, 0x4d, 0x44, 0x00, 0x00, 0x03              /* $78-$7d */
+                                                       };
+                                                       if(memcmp((void *)(t + 0x58), psyoblade_ipl1, sizeof(psyoblade_ipl1)) == 0) {
+                                                               is_special_disk = SPECIAL_DISK_FM77AV_PSYOBLADE;
+                                                               break;
+                                                       } else if(memcmp((void *)(t + 0x10), psyoblade_disk_1, sizeof(psyoblade_disk_1)) == 0) {
+                                                               if(memcmp((void *)(t + 0x40), psyoblade_disk_2, sizeof(psyoblade_disk_2)) == 0) {
+                                                                       is_special_disk = SPECIAL_DISK_FM77AV_PSYOBLADE;
+                                                                       break;
+                                                               }
+                                                       } else if(memcmp((void *)(t + 0x3e + 0x10), riglas_fm, sizeof(riglas_fm)) == 0) {
+                                                               is_special_disk = SPECIAL_DISK_FM7_RIGLAS;
+                                                               break;
+                                                       }
+                                       } else if(data_size.sd == 0x100 && t[0] == 0 && t[1] == 0 && t[2] == 3 && t[3] == 1) {
+                                                       static const uint8_t taiyoufm1[] = {
+                                                               0x10, 0xff, 0x04, 0x9f, 0x10, 0xce, 0xfc, 0xf4,
+                                                               0x37, 0x20, 0x34, 0x20, 0x37, 0x36, 0x34, 0x36, //7 4 7646
+                                                               0x37, 0x36, 0x34, 0x36, 0x37, 0x36, 0x34, 0x36, //76467646
+                                                               0x37, 0x36, 0x34, 0x36, 0x37, 0x36, 0x34, 0x36, //76467646
+                                                               0x37, 0x36, 0x34, 0x36, 0x37, 0x36, 0x34, 0x36, //76467646
+                                                               0x37, 0x36, 0x34, 0x36, 0x37, 0x36, 0x34, 0x36, //76467646
+                                                               0x37, 0x36, 0x34, 0x36, 0x37, 0x36, 0x34, 0x36, //76467646
+                                                               0x10, 0xfe, 0x04, 0x9f, 0x1c, 0xef, 0x86, 0xff,
+                                                               0xb7, 0xfc, 0xf8, 0x17, 0x03, 0x3d, 0x33, 0xc9,
+                                                               0xdb, 0x9c, 0x35, 0x04, 0x5a, 0x26, 0xa0, 0xfe,
+                                                       };
+                                                       if(memcmp((void *)(t + 0x70), taiyoufm1, sizeof(taiyoufm1)) == 0) {
+                                                               is_special_disk = SPECIAL_DISK_FM7_TAIYOU1;
+                                                               break;
+                                                       }
+                                               } else if(data_size.sd == 0x100 && t[0] == 0 && t[1] == 0 && t[2] == 2 && t[3] == 1) {
+                                                       static const uint8_t taiyoufm2[] = {
+                                                               0x3d, 0x02, 0xa3, 0xd6, 0x01, 0xc7, 0x06, 0x86,
+                                                               0x07, 0x00, 0x00, 0xc7, 0x06, 0xd4, 0x01, 0x00,
+                                                               0x00, 0xb4, 0x19, 0xcd, 0x21, 0xfe, 0xc0, 0xa2,
+                                                               0xda, 0x01, 0x06, 0xb9, 0x10, 0x00, 0xbb, 0x40,
+                                                               0x00, 0x8e, 0xc3, 0xbb, 0x00, 0x00, 0xfe, 0x06,
+                                                               0xd9, 0x01, 0x26, 0x80, 0xbf, 0x6c, 0x02, 0x00,
+                                                               0x74, 0x03, 0x43, 0xe2, 0xf1, 0x07, 0xc6, 0x06,
+                                                               0xdb, 0x01, 0x00, 0xbb, 0x80, 0x00, 0x80, 0x3f,
+                                                       };
+                                                       if(memcmp((void *)(t + 0x00), taiyoufm2, sizeof(taiyoufm2)) == 0) {
+                                                               is_special_disk = SPECIAL_DISK_FM7_TAIYOU2;
+                                                               break;
+                                                       }
+                                               }
+                                               t += data_size.sd + 0x10;
+                                       }
+                               }
+                       }
+               } else if(type_x1) { 
+//#elif defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
+                       // FIXME: ugly patch for X1turbo ALPHA and X1 Batten Tanuki
+                       if(media_type == MEDIA_TYPE_2D) {
+                               // check first track
+                               pair_t offset;
+                               offset.read_4bytes_le_from(buffer + 0x20);
+                               if(IS_VALID_TRACK(offset.d)) {
+                                       // check first sector
+                                       static const uint8_t batten[] = {0xca, 0xde, 0xaf, 0xc3, 0xdd, 0x20, 0xc0, 0xc7, 0xb7};
+                                       uint8_t *t = buffer + offset.d;
+//#if defined(_X1TURBO) || defined(_X1TURBOZ)
+//                             if(strncmp((char *)(t + 0x11), "turbo ALPHA", 11) == 0) {
+//                                     is_special_disk = SPECIAL_DISK_X1TURBO_ALPHA;
+//                             } else
+//#endif
+                                               if(memcmp((void *)(t + 0x11), batten, sizeof(batten)) == 0) {
+                                                       is_special_disk = SPECIAL_DISK_X1_BATTEN;
+                                               }
+                               }
+                       }
+               }
+//#endif
+       }
+}
+
+void DISK::close()
+{
+       // write disk image
+       if(inserted) {
+               if(trim_required) {
+                       trim_buffer();
+                       trim_required = false;
+               }
+               buffer[0x1a] = write_protected ? 0x10 : 0; // mey be changed
+               
+               if(/*!write_protected &&*/ !(file_size.d == orig_file_size && get_crc32(buffer, file_size.d) == orig_crc32)) {
+                       // write image
+                       FILEIO* fio = new FILEIO();
+                       int pre_size = 0, post_size = 0;
+                       uint8_t *pre_buffer = NULL, *post_buffer = NULL;
+                       
+                       // is this d88 format ?
+                       if(check_file_extension(dest_path, _T(".d88")) || check_file_extension(dest_path, _T(".d77")) || check_file_extension(dest_path, _T(".1dd"))) {
+                               if(fio->Fopen(dest_path, FILEIO_READ_BINARY)) {
+                                       fio->Fseek(0, FILEIO_SEEK_END);
+                                       uint32_t total_size = fio->Ftell(), offset = 0;
+                                       for(int i = 0; i < file_bank; i++) {
+                                               fio->Fseek(offset + 0x1c, SEEK_SET);
+                                               offset += fio->FgetUint32_LE();
+                                       }
+                                       if((pre_size = offset) > 0) {
+                                               pre_buffer = (uint8_t *)malloc(pre_size);
+                                               fio->Fseek(0, FILEIO_SEEK_SET);
+                                               fio->Fread(pre_buffer, pre_size, 1);
+                                       }
+                                       fio->Fseek(offset + 0x1c, SEEK_SET);
+                                       offset += fio->FgetUint32_LE();
+                                       if((post_size = total_size - offset) > 0) {
+                                               post_buffer = (uint8_t *)malloc(post_size);
+                                               fio->Fseek(offset, FILEIO_SEEK_SET);
+                                               fio->Fread(post_buffer, post_size, 1);
+                                       }
+                                       fio->Fclose();
+                               }
+                       }
+                       
+                       // is this solid image and was physical formatted ?
+                       if(is_solid_image) {
+                               bool formatted = false;
+                               int tracks = 0;
+                               
+                               for(int trkside = 0; trkside < 164; trkside++) {
+                                       pair_t offset;
+                                       offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+                                       
+                                       if(!IS_VALID_TRACK(offset.d)) {
+                                               continue;
+                                       }
+                                       if(solid_nside == 1 && (trkside & 1) == 1) {
+                                               formatted = true;
+                                       }
+                                       tracks++;
+                                       
+                                       uint8_t* t = buffer + offset.d;
+                                       pair_t sector_num, data_size;
+                                       sector_num.read_2bytes_le_from(t + 4);
+                                       
+                                       if(sector_num.sd != solid_nsec) {
+                                               formatted = true;
+                                       }
+                                       for(int i = 0; i < sector_num.sd; i++) {
+                                               data_size.read_2bytes_le_from(t + 14);
+                                               if(data_size.sd != solid_size) {
+                                                       formatted = true;
+                                               }
+                                               if(t[6] != (solid_mfm ? 0 : 0x40)) {
+                                                       formatted = true;
+                                               }
+                                               t += data_size.sd + 0x10;
+                                       }
+                               }
+                               if(tracks != (solid_ncyl * solid_nside)) {
+                                       formatted = true;
+                               }
+                               if(formatted) {
+                                       my_stprintf_s(dest_path, _MAX_PATH, _T("%s.D88"), orig_path);
+                                       is_solid_image = false;
+                               }
+                       }
+                       
+                       if((FILEIO::IsFileExisting(dest_path) && FILEIO::IsFileProtected(dest_path)) || !fio->Fopen(dest_path, FILEIO_WRITE_BINARY)) {
+                               fio->Fopen(local_path(create_string(_T("temporary_saved_floppy_disk_#%d.d88"), drive_num)), FILEIO_WRITE_BINARY);
+                       }
+                       if(fio->IsOpened()) {
+                               if(pre_buffer) {
+                                       fio->Fwrite(pre_buffer, pre_size, 1);
+                               }
+                               if(is_solid_image) {
+                                       if(is_fdi_image) {
+                                               fio->Fwrite(fdi_header, 4096, 1);
+                                       }
+                                       for(int trkside = 0; trkside < 164; trkside++) {
+                                               pair_t offset;
+                                               offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+                                               
+                                               if(!IS_VALID_TRACK(offset.d)) {
+                                                       continue;
+                                               }
+                                               uint8_t* t = buffer + offset.d;
+                                               pair_t sector_num, data_size;
+                                               sector_num.read_2bytes_le_from(t + 4);
+                                               
+                                               for(int i = 0; i < sector_num.sd; i++) {
+                                                       data_size.read_2bytes_le_from(t + 14);
+                                                       fio->Fwrite(t + 0x10, data_size.sd, 1);
+                                                       t += data_size.sd + 0x10;
+                                               }
+                                       }
+                               } else {
+                                       fio->Fwrite(buffer, file_size.d, 1);
+                               }
+                               if(post_buffer) {
+                                       fio->Fwrite(post_buffer, post_size, 1);
+                               }
+                               fio->Fclose();
+                       }
+                       if(pre_buffer) {
+                               free(pre_buffer);
+                       }
+                       if(post_buffer) {
+                               free(post_buffer);
+                       }
+                       delete fio;
+               }
+               ejected = true;
+       }
+       inserted = write_protected = false;
+       file_size.d = 0;
+       sector_size.sd = sector_num.sd = 0;
+       sector = NULL;
+}
+
+//#ifdef _ANY2D88
+void DISK::save_as_d88(const _TCHAR* file_path)
+{
+//#ifdef _ANY2D88
+       if(inserted) {
+               FILEIO* fio = new FILEIO();
+               if(fio->Fopen(file_path, FILEIO_WRITE_BINARY)) {
+                       if(is_1dd_image) {
+                               memcpy(tmp_buffer, buffer + 0x20, 4 * 82);
+                               for(int trk = 0; trk < 82; trk++) {
+                                       memcpy(buffer + 0x20 + (trk * 2 + 0) * 4, tmp_buffer + trk * 4, 4);
+                                       memset(buffer + 0x20 + (trk * 2 + 1) * 4, 0, 4);
+                               }
+                               buffer[0x1b] = MEDIA_TYPE_2DD;
+                       }
+                       fio->Fwrite(buffer, file_size.d, 1);
+                       fio->Fclose();
+               }
+               delete fio;
+       }
+//#endif
+}
+//#endif
+
+bool DISK::get_track(int trk, int side)
+{
+       if(media_type == MEDIA_TYPE_2D && drive_type == DRIVE_TYPE_2DD) {
+               if(trk >= 0) {
+                       if(trk & 1) {
+                               return false; // unformat
+                       }
+                       trk >>= 1;
+               }
+       } else if(media_type == MEDIA_TYPE_2DD && drive_type == DRIVE_TYPE_2D) {
+               if(trk >= 0) trk <<= 1;
+       }
+       return get_track_tmp(trk, side);
+}
+
+bool DISK::get_track_tmp(int trk, int side)
+{
+       sector_size.sd = sector_num.sd = 0;
+       invalid_format = false;
+//     no_skew = true;
+       
+       // disk not inserted or invalid media type
+       if(!(inserted && check_media_type())) {
+               return false;
+       }
+       
+       // search track
+       int trkside = is_1dd_image ? trk : (trk * 2 + (side & 1));
+       if(!(0 <= trkside && trkside < 164)) {
+               return false;
+       }
+       cur_track = trk;
+       cur_side = side;
+       
+       pair_t offset;
+       offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+       
+       if(!IS_VALID_TRACK(offset.d)) {
+               return false;
+       }
+       
+       // track found
+       sector = buffer + offset.d;
+       sector_num.read_2bytes_le_from(sector + 4);
+       pair_t data_size;
+       data_size.read_2bytes_le_from(sector + 14);
+       
+       // create each sector position in track
+       track_mfm = false;
+       if(sector_num.sd == 0) {
+               track_mfm = drive_mfm;
+       } else {
+               uint8_t* t = sector;
+               for(int i = 0; i < sector_num.sd; i++) {
+                       data_size.read_2bytes_le_from(t + 14);
+                       // t[6]: 0x00 = double-density, 0x40 = single-density
+                       if(t[6] == 0x00) {
+                               track_mfm = true;
+                               break;
+                       }
+                       t += data_size.sd + 0x10;
+               }
+       }
+       int sync_size  = track_mfm ? 12 : 6;
+       int am_size = track_mfm ? 3 : 0;
+       int gap0_size = track_mfm ? 80 : 40;
+       int gap1_size = track_mfm ? 50 : 26;
+       int gap2_size = track_mfm ? 22 : 11;
+       int gap3_size = 0, gap4_size;
+       
+       if(media_type == MEDIA_TYPE_144 || media_type == MEDIA_TYPE_2HD) {
+               if(track_mfm) {
+                       if(data_size.sd ==  256 && sector_num.sd == 26) gap3_size =  54;
+                       if(data_size.sd ==  512 && sector_num.sd == 15) gap3_size =  84;
+                       if(data_size.sd == 1024 && sector_num.sd ==  8) gap3_size = 116;
+               } else {
+                       if(data_size.sd ==  128 && sector_num.sd == 26) gap3_size =  27;
+                       if(data_size.sd ==  256 && sector_num.sd == 15) gap3_size =  42;
+                       if(data_size.sd ==  512 && sector_num.sd ==  8) gap3_size =  58;
+               }
+       } else {
+               if(track_mfm) {
+                       if(data_size.sd ==  256 && sector_num.sd == 16) gap3_size =  51;
+                       if(data_size.sd ==  512 && sector_num.sd ==  9) gap3_size =  80;
+                       if(data_size.sd == 1024 && sector_num.sd ==  5) gap3_size = 116;
+               } else {
+                       if(data_size.sd ==  128 && sector_num.sd == 16) gap3_size =  27;
+                       if(data_size.sd ==  256 && sector_num.sd ==  9) gap3_size =  42;
+                       if(data_size.sd ==  512 && sector_num.sd ==  5) gap3_size =  58;
+               }
+       }
+       
+       uint8_t* t = sector;
+       int total = 0, valid_sector_num = 0;
+       
+       for(int i = 0; i < sector_num.sd; i++) {
+               data_size.read_2bytes_le_from(t + 14);
+               sync_position[i] = total; // for invalid format case
+               total += sync_size + (am_size + 1) + (4 + 2) + gap2_size;
+               if(data_size.sd > 0) {
+                       total += sync_size + (am_size + 1);
+                       total += data_size.sd + 2;
+                       valid_sector_num++;
+               }
+//             if(t[2] != i + 1) {
+//                     no_skew = false;
+//             }
+               t += data_size.sd + 0x10;
+       }
+       total += sync_size + (am_size + 1); // sync in preamble
+       
+       if(gap3_size == 0) {
+               gap3_size = (get_track_size() - total - gap0_size - gap1_size) / (valid_sector_num + 1);
+       }
+       gap4_size = get_track_size() - total - gap0_size - gap1_size - gap3_size * valid_sector_num;
+       
+       if(gap3_size < 8 || gap4_size < 8) {
+               gap0_size = gap1_size = gap3_size = (get_track_size() - total) / (2 + valid_sector_num + 1);
+               gap4_size = get_track_size() - total - gap0_size - gap1_size - gap3_size * valid_sector_num;
+       }
+       if(gap3_size < 8 || gap4_size < 8) {
+               gap0_size = gap1_size = gap3_size = gap4_size = 8;
+               invalid_format = true;
+       }
+       int preamble_size = gap0_size + sync_size + (am_size + 1) + gap1_size;
+       
+       if(invalid_format) {
+               total -= sync_size + (am_size + 1);
+               for(int i = 0; i < sector_num.sd; i++) {
+                       sync_position[i] *= get_track_size() - preamble_size - gap4_size;
+                       sync_position[i] /= total;
+               }
+       }
+       t = sector;
+       total = preamble_size;
+       sync_position[array_length(sync_position) - 1] = gap0_size; // sync position in preamble
+       
+       for(int i = 0; i < sector_num.sd; i++) {
+               data_size.read_2bytes_le_from(t + 14);
+               if(invalid_format) {
+                       total = preamble_size + sync_position[i];
+               }
+               sync_position[i] = total;
+               total += sync_size;
+               am1_position[i] = total;
+               total += am_size + 1;
+               id_position[i] = total;
+               total += (4 + 2) + gap2_size;
+               if(data_size.sd > 0) {
+                       total += sync_size + (am_size + 1);
+                       data_position[i] = total;
+                       total += data_size.sd + 2;
+                       total += gap3_size;
+               } else {
+                       data_position[i] = total; // FIXME
+               }
+               t += data_size.sd + 0x10;
+       }
+       return true;
+}
+
+bool DISK::make_track(int trk, int side)
+{
+       if(media_type == MEDIA_TYPE_2D && drive_type == DRIVE_TYPE_2DD) {
+               if(trk >= 0) {
+                       if(trk & 1) {
+                               return false; // unformat
+                       }
+                       trk >>= 1;
+               }
+       } else if(media_type == MEDIA_TYPE_2DD && drive_type == DRIVE_TYPE_2D) {
+               if(trk >= 0) trk <<= 1;
+       }
+       return make_track_tmp(trk, side);
+}
+
+bool DISK::make_track_tmp(int trk, int side)
+{
+       int track_size = get_track_size();
+       
+       if(!get_track_tmp(trk, side)) {
+               // create a dummy track
+               for(int i = 0; i < track_size; i++) {
+                       track[i] = rand();
+               }
+               return false;
+       }
+       
+       // make track image
+       int sync_size  = track_mfm ? 12 : 6;
+       int am_size = track_mfm ? 3 : 0;
+       int gap2_size = track_mfm ? 22 : 11;
+       uint8_t gap_data = track_mfm ? 0x4e : 0xff;
+       
+       // preamble
+       memset(track, gap_data, track_size);
+       int q = sync_position[array_length(sync_position) - 1];
+       
+       // sync
+       for(int i = 0; i < sync_size; i++) {
+               track[q++] = 0x00;
+       }
+       // index mark
+       for(int i = 0; i < am_size; i++) {
+               track[q++] = 0xc2;
+       }
+       track[q++] = 0xfc;
+       
+       // sectors
+       uint8_t *t = sector;
+       
+       for(int i = 0; i < sector_num.sd; i++) {
+               pair_t data_size;
+               data_size.read_2bytes_le_from(t + 14);
+               int p = sync_position[i];
+               
+               // sync
+               for(int j = 0; j < sync_size; j++) {
+                       if(p < track_size) track[p++] = 0x00;
+               }
+               // am1
+               uint16_t crc = 0xffff;
+               for(int j = 0; j < am_size; j++) {
+                       if(p < track_size) track[p++] = 0xa1;
+                       crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ 0xa1]);
+               }
+               if(p < track_size) track[p++] = 0xfe;
+               crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ 0xfe]);
+               // id
+               if(p < track_size) track[p++] = t[0];
+               if(p < track_size) track[p++] = t[1];
+               if(p < track_size) track[p++] = t[2];
+               if(p < track_size) track[p++] = t[3];
+               crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[0]]);
+               crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[1]]);
+               crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[2]]);
+               crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[3]]);
+               if(p < track_size) track[p++] = (crc >> 8) & 0xff;
+               if(p < track_size) track[p++] = (crc >> 0) & 0xff;
+               // gap2
+               for(int j = 0; j < gap2_size; j++) {
+                       if(p < track_size) track[p++] = gap_data;
+               }
+               // data field
+               if(data_size.sd > 0) {
+                       // sync
+                       for(int j = 0; j < sync_size; j++) {
+                               if(p < track_size) track[p++] = 0x00;
+                       }
+                       // am2
+                       crc = 0xffff;
+                       for(int j = 0; j < am_size; j++) {
+                               if(p < track_size) track[p++] = 0xa1;
+                               crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ 0xa1]);
+                       }
+                       uint8_t am2 = (t[7] != 0) ? 0xf8 : 0xfb;
+                       if(p < track_size) track[p++] = am2;
+                       crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ am2]);
+                       // data
+                       for(int j = 0; j < data_size.sd; j++) {
+                               if(p < track_size) track[p++] = t[0x10 + j];
+                               crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[0x10 + j]]);
+                       }
+                       if(p < track_size) track[p++] = (crc >> 8) & 0xff;
+                       if(p < track_size) track[p++] = (crc >> 0) & 0xff;
+               }
+               t += data_size.sd + 0x10;
+       }
+       return true;
+}
+
+bool DISK::get_sector(int trk, int side, int index)
+{
+       if(media_type == MEDIA_TYPE_2D && drive_type == DRIVE_TYPE_2DD) {
+               if(trk >= 0) {
+                       if(trk & 1) {
+                               return false; // unformat
+                       }
+                       trk >>= 1;
+               }
+       } else if(media_type == MEDIA_TYPE_2DD && drive_type == DRIVE_TYPE_2D) {
+               if(trk >= 0) trk <<= 1;
+       }
+       return get_sector_tmp(trk, side, index);
+}
+
+bool DISK::get_sector_tmp(int trk, int side, int index)
+{
+       sector_size.sd = sector_num.sd = 0;
+       sector = NULL;
+       
+       // disk not inserted or invalid media type
+       if(!(inserted && check_media_type())) {
+               return false;
+       }
+       
+       // search track
+       if(trk == -1 && side == -1) {
+               trk = cur_track;
+               side = cur_side;
+       }
+       int trkside = is_1dd_image ? trk : (trk * 2 + (side & 1));
+       if(!(0 <= trkside && trkside < 164)) {
+               return false;
+       }
+       pair_t offset;
+       offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+       
+       if(!IS_VALID_TRACK(offset.d)) {
+               return false;
+       }
+       
+       // track found
+       uint8_t* t = buffer + offset.d;
+       sector_num.read_2bytes_le_from(t + 4);
+       
+       if(index >= sector_num.sd) {
+               return false;
+       }
+       
+       // skip sector
+       for(int i = 0; i < index; i++) {
+               pair_t data_size;
+               data_size.read_2bytes_le_from(t + 14);
+               t += data_size.sd + 0x10;
+       }
+       set_sector_info(t);
+       return true;
+}
+
+void DISK::set_sector_info(uint8_t *t)
+{
+       // header info
+       int am_size = track_mfm ? 3 : 0;
+       uint16_t crc = 0xffff;
+       for(int i = 0; i < am_size; i++) {
+               crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ 0xa1]);
+       }
+       crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ 0xfe]);
+       id[0] = t[0];
+       id[1] = t[1];
+       id[2] = t[2];
+       id[3] = t[3];
+       crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[0]]);
+       crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[1]]);
+       crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[2]]);
+       crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)(crc >> 8) ^ t[3]]);
+       id[4] = (crc >> 8) & 0xff;
+       id[5] = (crc >> 0) & 0xff;
+       // http://www.gnu-darwin.or.jp/www001/src/ports/emulators/quasi88/work/quasi88-0.6.3/document/FORMAT.TXT
+       // t[6]: 0x00 = double-density, 0x40 = single-density
+       // t[7]: 0x00 = normal, 0x10 = deleted mark
+       // t[8]: 0x00 = valid, 0x10 = valid (deleted data), 0xa0 = id crc error, 0xb0 = data crc error, 0xe0 = address mark missing, 0xf0 = data mark missing
+       density = t[6];
+       deleted = (t[7] != 0);
+//     if(ignore_crc()) {
+//             addr_crc_error = false;
+//             data_crc_error = false;
+//     } else {
+               addr_crc_error = ((t[8] & 0xf0) == 0xa0);
+               data_crc_error = ((t[8] & 0xf0) == 0xb0);
+//     }
+       sector = t + 0x10;
+       sector_size.read_2bytes_le_from(t + 14);
+}
+
+void DISK::set_deleted(bool value)
+{
+       if(sector != NULL) {
+               uint8_t *t = sector - 0x10;
+               t[7] = value ? 0x10 : 0;
+               if((t[8] & 0xf0) == 0x00 || (t[8] & 0xf0) == 0x10) {
+                       t[8] = (t[8] & 0x0f) | t[7];
+               }
+       }
+       deleted = value;
+}
+
+void DISK::set_data_crc_error(bool value)
+{
+       if(sector != NULL) {
+               uint8_t *t = sector - 0x10;
+               t[8] = (t[8] & 0x0f) | (value ? 0xb0 : t[7]);
+       }
+       data_crc_error = value;
+}
+
+void DISK::set_data_mark_missing()
+{
+       if(sector != NULL) {
+               uint8_t *t = sector - 0x10;
+               t[8] = (t[8] & 0x0f) | 0xf0;
+               t[14] = t[15] = 0;
+       }
+//     addr_crc_error = false;
+       data_crc_error = false;
+}
+
+bool DISK::format_track(int trk, int side)
+{
+       if(media_type == MEDIA_TYPE_2D && drive_type == DRIVE_TYPE_2DD) {
+               if(trk >= 0) {
+                       if(trk & 1) {
+                               return false; // unformat
+                       }
+                       trk >>= 1;
+               }
+       } else if(media_type == MEDIA_TYPE_2DD && drive_type == DRIVE_TYPE_2D) {
+               if(trk >= 0) trk <<= 1;
+       }
+       return format_track_tmp(trk, side);
+}
+
+bool DISK::format_track_tmp(int trk, int side)
+{
+       // disk not inserted or invalid media type
+       if(!(inserted && check_media_type())) {
+               return false;
+       }
+       
+       // search track
+       int trkside = is_1dd_image ? trk : (trk * 2 + (side & 1));
+       if(!(0 <= trkside && trkside < 164)) {
+               return false;
+       }
+       
+       // create new empty track
+       if(trim_required) {
+               trim_buffer();
+               trim_required = false;
+       }
+       memset(buffer + DISK_BUFFER_SIZE, 0, sizeof(buffer) - DISK_BUFFER_SIZE);
+       pair_t offset;
+       offset.d = DISK_BUFFER_SIZE;
+       offset.write_4bytes_le_to(buffer + 0x20 + trkside * 4);
+       
+       trim_required = true;
+       sector_num.sd = 0;
+       track_mfm = drive_mfm;
+       
+       return true;
+}
+
+void DISK::insert_sector(uint8_t c, uint8_t h, uint8_t r, uint8_t n, bool deleted, bool data_crc_error, uint8_t fill_data, int length)
+{
+       uint8_t* t = buffer + DISK_BUFFER_SIZE;
+       
+       sector_num.sd++;
+       for(int i = 0; i < (sector_num.sd - 1); i++) {
+               t[4] = sector_num.b.l;
+               t[5] = sector_num.b.h;
+               pair_t data_size;
+               data_size.read_2bytes_le_from(t + 14);
+               t += data_size.sd + 0x10;
+       }
+       t[0] = c;
+       t[1] = h;
+       t[2] = r;
+       t[3] = n;
+       t[4] = sector_num.b.l;
+       t[5] = sector_num.b.h;
+       t[6] = track_mfm ? 0 : 0x40;
+       t[7] = deleted ? 0x10 : 0;
+       t[8] = data_crc_error ? 0xb0 : t[7];
+       t[14] = (length >> 0) & 0xff;
+       t[15] = (length >> 8) & 0xff;
+       memset(t + 16, fill_data, length);
+       
+       set_sector_info(t);
+}
+
+void DISK::sync_buffer()
+{
+       if(trim_required) {
+               trim_buffer();
+               trim_required = false;
+       }
+}
+
+void DISK::trim_buffer()
+{
+       int max_tracks = 164;
+       int track_limit = 164;
+       uint32_t dest_offset = 0x2b0;
+       
+       // copy header
+       memset(tmp_buffer, 0, sizeof(tmp_buffer));
+       memcpy(tmp_buffer, buffer, 0x20);
+       
+       // check max tracks
+       if(media_type == MEDIA_TYPE_2D) {
+               track_limit = 84;
+       }
+       for(int trkside = 0; trkside < 164; trkside++) {
+               pair_t src_trk_offset;
+               src_trk_offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+               if(src_trk_offset.d != 0) {
+#if 1
+                       if(src_trk_offset.d < 0x2b0) {
+                               max_tracks = (src_trk_offset.d - 0x20) >> 2;
+                               if(max_tracks > track_limit) {
+                                       max_tracks = track_limit;
+                               }
+                       }
+#else
+                       if(src_trk_offset.d != 0x2b0) {
+                               max_tracks = (src_trk_offset.d - 0x20) >> 2;
+                               if(max_tracks > 164) {
+                                       dest_offset = 0x20 + max_tracks * 4);
+                               }
+                       }
+#endif
+                       break;
+               }
+       }
+       
+       // copy tracks
+       for(int trkside = 0; trkside < max_tracks; trkside++) {
+               pair_t src_trk_offset;
+               src_trk_offset.read_4bytes_le_from(buffer + 0x20 + trkside * 4);
+               
+               pair_t dest_trk_offset;
+               dest_trk_offset.d = 0;
+               
+               if(IS_VALID_TRACK(src_trk_offset.d)) {
+                       uint8_t* t = buffer + src_trk_offset.d;
+                       pair_t sector_num, data_size;
+                       sector_num.read_2bytes_le_from(t + 4);
+                       if(sector_num.sd != 0) {
+                               dest_trk_offset.d = dest_offset;
+                               for(int i = 0; i < sector_num.sd; i++) {
+                                       data_size.read_2bytes_le_from(t + 14);
+                                       memcpy(tmp_buffer + dest_offset, t, data_size.sd + 0x10);
+                                       dest_offset += data_size.sd + 0x10;
+                                       t += data_size.sd + 0x10;
+                               }
+                       }
+               }
+               dest_trk_offset.write_4bytes_le_to(tmp_buffer + 0x20 + trkside * 4);
+       }
+       
+       // update file size
+       file_size.d = dest_offset;
+       file_size.write_4bytes_le_to(tmp_buffer + 0x1c);
+       
+       memset(buffer, 0, sizeof(buffer));
+       memcpy(buffer, tmp_buffer, min((unsigned int)sizeof(buffer), file_size.d));
+}
+
+int DISK::get_max_tracks()
+{
+       if(drive_type != DRIVE_TYPE_UNK) {
+               return (drive_type != DRIVE_TYPE_2D) ? 84 : 42;
+       } else if(inserted) {
+               return (media_type != MEDIA_TYPE_2D) ? 84 : 42;
+       } else {
+               return 84; // 2DD or 2HD
+       }
+}
+
+int DISK::get_rpm()
+{
+       if(drive_rpm != 0) {
+               return drive_rpm;
+       } else if(inserted) {
+               return (media_type == MEDIA_TYPE_2HD) ? 360 : 300;
+       } else {
+               return (drive_type == DRIVE_TYPE_2HD) ? 360 : 300;
+       }
+}
+
+int DISK::get_track_size()
+{
+       if(track_size != 0) {
+               return track_size;
+       } else if(inserted) {
+               return media_type == MEDIA_TYPE_144 ? 12500 : media_type == MEDIA_TYPE_2HD ? 10410 : track_mfm ? 6250 : 3100;
+       } else {
+               return drive_type == DRIVE_TYPE_144 ? 12500 : drive_type == DRIVE_TYPE_2HD ? 10410 : drive_mfm ? 6250 : 3100;
+       }
+}
+
+double DISK::get_usec_per_track()
+{
+       return 1000000.0 / (get_rpm() / 60.0);
+}
+
+double DISK::get_usec_per_bytes(int bytes)
+{
+       return 1000000.0 / (get_track_size() * (get_rpm() / 60.0)) * bytes;
+}
+
+int DISK::get_bytes_per_usec(double usec)
+{
+       return (int)(usec / get_usec_per_bytes(1) + 0.5);
+}
+
+bool DISK::check_media_type()
+{
+       switch(drive_type) {
+       case DRIVE_TYPE_2D:
+                       return (media_type == MEDIA_TYPE_2D || media_type == MEDIA_TYPE_2DD);
+       case DRIVE_TYPE_2DD:
+               return (media_type == MEDIA_TYPE_2D || media_type == MEDIA_TYPE_2DD);
+       case DRIVE_TYPE_2HD:
+               return (media_type == MEDIA_TYPE_2HD);
+       case DRIVE_TYPE_144:
+               return (media_type == MEDIA_TYPE_144);
+       case DRIVE_TYPE_UNK:
+               return true; // always okay
+       }
+       return false;
+}
+
+// image decoder
+
+#define COPYBUFFER(src, size) { \
+       if(file_size.d + (size) > DISK_BUFFER_SIZE) { \
+               return false; \
+       } \
+       memcpy(buffer + file_size.d, (src), (size)); \
+       file_size.d += (size); \
+}
+
+typedef struct {
+       char title[17];
+       uint8_t rsrv[9];
+       uint8_t protect;
+       uint8_t type;
+       uint32_t size;
+       uint32_t trkptr[164];
+} d88_hdr_t;
+
+typedef struct {
+       uint8_t c, h, r, n;
+       uint16_t nsec;
+       uint8_t dens, del, stat;
+       uint8_t rsrv[5];
+       uint16_t size;
+} d88_sct_t;
+
+// teledisk image decoder
+
+/*
+       this teledisk image decoder is based on:
+       
+               LZHUF.C English version 1.0 based on Japanese version 29-NOV-1988
+               LZSS coded by Haruhiko OKUMURA
+               Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI
+               Edited and translated to English by Kenji RIKITAKE
+               TDLZHUF.C by WTK
+*/
+
+#define STRING_BUFFER_SIZE     4096
+#define LOOKAHEAD_BUFFER_SIZE  60
+#define THRESHOLD              2
+#define N_CHAR                 (256 - THRESHOLD + LOOKAHEAD_BUFFER_SIZE)
+#define TABLE_SIZE             (N_CHAR * 2 - 1)
+#define ROOT_POSITION          (TABLE_SIZE - 1)
+#define MAX_FREQ               0x8000
+
+static uint8_t td_text_buf[STRING_BUFFER_SIZE + LOOKAHEAD_BUFFER_SIZE - 1];
+static uint16_t td_ptr;
+static uint16_t td_bufcnt, td_bufndx, td_bufpos;
+static uint16_t td_ibufcnt, td_ibufndx;
+static uint8_t td_inbuf[512];
+static uint16_t td_freq[TABLE_SIZE + 1];
+static short td_prnt[TABLE_SIZE + N_CHAR];
+static short td_son[TABLE_SIZE];
+static uint16_t td_getbuf;
+static uint8_t td_getlen;
+
+static const uint8_t td_d_code[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+       0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+       0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+       0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
+       0x18, 0x18, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x1f,
+       0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
+       0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f,
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
+};
+static const uint8_t td_d_len[256] = {
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+};
+
+static int td_next_word(FILEIO* fio)
+{
+       if(td_ibufndx >= td_ibufcnt) {
+               td_ibufndx = td_ibufcnt = 0;
+               memset(td_inbuf, 0, 512);
+               for(int i = 0; i < 512; i++) {
+                       int d = fio->Fgetc();
+                       if(d == EOF) {
+                               if(i) {
+                                       break;
+                               }
+                               return(-1);
+                       }
+                       td_inbuf[i] = d;
+                       td_ibufcnt = i + 1;
+               }
+       }
+       while(td_getlen <= 8) {
+               td_getbuf |= td_inbuf[td_ibufndx++] << (8 - td_getlen);
+               td_getlen += 8;
+       }
+       return 0;
+}
+
+static int td_get_bit(FILEIO* fio)
+{
+       if(td_next_word(fio) < 0) {
+               return -1;
+       }
+       short i = td_getbuf;
+       td_getbuf <<= 1;
+       td_getlen--;
+       return (i < 0) ? 1 : 0;
+}
+
+static int td_get_byte(FILEIO* fio)
+{
+       if(td_next_word(fio) != 0) {
+               return -1;
+       }
+       uint16_t i = td_getbuf;
+       td_getbuf <<= 8;
+       td_getlen -= 8;
+       i >>= 8;
+       return (int)i;
+}
+
+static void td_start_huff()
+{
+       int i, j;
+       for(i = 0; i < N_CHAR; i++) {
+               td_freq[i] = 1;
+               td_son[i] = i + TABLE_SIZE;
+               td_prnt[i + TABLE_SIZE] = i;
+       }
+       i = 0; j = N_CHAR;
+       while(j <= ROOT_POSITION) {
+               td_freq[j] = td_freq[i] + td_freq[i + 1];
+               td_son[j] = i;
+               td_prnt[i] = td_prnt[i + 1] = j;
+               i += 2; j++;
+       }
+       td_freq[TABLE_SIZE] = 0xffff;
+       td_prnt[ROOT_POSITION] = 0;
+}
+
+static void td_reconst()
+{
+       short i, j = 0, k;
+       uint16_t f, l;
+       for(i = 0; i < TABLE_SIZE; i++) {
+               if(td_son[i] >= TABLE_SIZE) {
+                       td_freq[j] = (td_freq[i] + 1) / 2;
+                       td_son[j] = td_son[i];
+                       j++;
+               }
+       }
+       for(i = 0, j = N_CHAR; j < TABLE_SIZE; i += 2, j++) {
+               k = i + 1;
+               f = td_freq[j] = td_freq[i] + td_freq[k];
+               for(k = j - 1; f < td_freq[k]; k--);
+               k++;
+               l = (j - k) * 2;
+               memmove(&td_freq[k + 1], &td_freq[k], l);
+               td_freq[k] = f;
+               memmove(&td_son[k + 1], &td_son[k], l);
+               td_son[k] = i;
+       }
+       for(i = 0; i < TABLE_SIZE; i++) {
+               if((k = td_son[i]) >= TABLE_SIZE) {
+                       td_prnt[k] = i;
+               } else {
+                       td_prnt[k] = td_prnt[k + 1] = i;
+               }
+       }
+}
+
+static void td_update(int c)
+{
+       int i, j, k, l;
+       if(td_freq[ROOT_POSITION] == MAX_FREQ) {
+               td_reconst();
+       }
+       c = td_prnt[c + TABLE_SIZE];
+       do {
+               k = ++td_freq[c];
+               if(k > td_freq[l = c + 1]) {
+                       while(k > td_freq[++l]);
+                       l--;
+                       td_freq[c] = td_freq[l];
+                       td_freq[l] = k;
+                       i = td_son[c];
+                       td_prnt[i] = l;
+                       if(i < TABLE_SIZE) {
+                               td_prnt[i + 1] = l;
+                       }
+                       j = td_son[l];
+                       td_son[l] = i;
+                       td_prnt[j] = c;
+                       if(j < TABLE_SIZE) {
+                               td_prnt[j + 1] = c;
+                       }
+                       td_son[c] = j;
+                       c = l;
+               }
+       }
+       while((c = td_prnt[c]) != 0);
+}
+
+static short td_decode_char(FILEIO* fio)
+{
+       int ret;
+       uint16_t c = td_son[ROOT_POSITION];
+       while(c < TABLE_SIZE) {
+               if((ret = td_get_bit(fio)) < 0) {
+                       return -1;
+               }
+               c += (unsigned)ret;
+               c = td_son[c];
+       }
+       c -= TABLE_SIZE;
+       td_update(c);
+       return c;
+}
+
+static short td_decode_position(FILEIO* fio)
+{
+       short bit;
+       uint16_t i, j, c;
+       if((bit = td_get_byte(fio)) < 0) {
+               return -1;
+       }
+       i = (uint16_t)bit;
+       c = (uint16_t)td_d_code[i] << 6;
+       j = td_d_len[i] - 2;
+       while(j--) {
+               if((bit = td_get_bit(fio)) < 0) {
+                        return -1;
+               }
+               i = (i << 1) + bit;
+       }
+       return (c | (i & 0x3f));
+}
+
+static void td_init_decode()
+{
+       td_ibufcnt= td_ibufndx = td_bufcnt = td_getbuf = 0;
+       td_getlen = 0;
+       td_start_huff();
+       for(int i = 0; i < STRING_BUFFER_SIZE - LOOKAHEAD_BUFFER_SIZE; i++) {
+               td_text_buf[i] = ' ';
+       }
+       td_ptr = STRING_BUFFER_SIZE - LOOKAHEAD_BUFFER_SIZE;
+}
+
+static int td_decode(FILEIO* fio, uint8_t *buf, int len)
+{
+       short c, pos;
+       int  count;
+       for(count = 0; count < len;) {
+               if(td_bufcnt == 0) {
+                       if((c = td_decode_char(fio)) < 0) {
+                               return count;
+                       }
+                       if(c < 256) {
+                               *(buf++) = (uint8_t)c;
+                               td_text_buf[td_ptr++] = (uint8_t)c;
+                               td_ptr &= (STRING_BUFFER_SIZE - 1);
+                               count++;
+                       } else {
+                               if((pos = td_decode_position(fio)) < 0) {
+                                       return count;
+                               }
+                               td_bufpos = (td_ptr - pos - 1) & (STRING_BUFFER_SIZE - 1);
+                               td_bufcnt = c - 255 + THRESHOLD;
+                               td_bufndx = 0;
+                       }
+               } else {
+                       while(td_bufndx < td_bufcnt && count < len) {
+                               c = td_text_buf[(td_bufpos + td_bufndx) & (STRING_BUFFER_SIZE - 1)];
+                               *(buf++) = (uint8_t)c;
+                               td_bufndx++;
+                               td_text_buf[td_ptr++] = (uint8_t)c;
+                               td_ptr &= (STRING_BUFFER_SIZE - 1);
+                               count++;
+                       }
+                       if(td_bufndx >= td_bufcnt) {
+                               td_bufndx = td_bufcnt = 0;
+                       }
+               }
+       }
+       return count;
+}
+
+typedef struct {
+       char sig[3];
+       uint8_t unknown;
+       uint8_t ver;
+       uint8_t dens;
+       uint8_t type;
+       uint8_t flag;
+       uint8_t dos;
+       uint8_t sides;
+       uint16_t crc;
+} td_hdr_t;
+
+typedef struct {
+       uint16_t crc;
+       uint16_t len;
+       uint8_t ymd[3];
+       uint8_t hms[3];
+} td_cmt_t;
+
+typedef struct {
+       uint8_t nsec, trk, head;
+       uint8_t crc;
+} td_trk_t;
+
+typedef struct {
+       uint8_t c, h, r, n;
+       uint8_t ctrl, crc;
+} td_sct_t;
+
+bool DISK::teledisk_to_d88(FILEIO *fio)
+{
+       td_hdr_t hdr;
+       td_cmt_t cmt;
+       td_trk_t trk;
+       td_sct_t sct;
+       uint8_t obuf[512];
+       bool temporary = false;
+       
+       // check teledisk header
+       fio->Fseek(0, FILEIO_SEEK_SET);
+       fio->Fread(&hdr, sizeof(td_hdr_t), 1);
+       if(hdr.sig[0] == 't' && hdr.sig[1] == 'd') {
+               // this image is compressed
+               // decompress to the temporary file
+               FILEIO* fio_tmp = new FILEIO();
+               if(!fio_tmp->Fopen(local_path(_T("teledisk.$$$")), FILEIO_WRITE_BINARY)) {
+                       delete fio_tmp;
+                       return false;
+               }
+               int rd = 1;
+               td_init_decode();
+               do {
+                       if((rd = td_decode(fio, obuf, 512)) > 0) {
+                               fio_tmp->Fwrite(obuf, rd, 1);
+                       }
+               }
+               while(rd > 0);
+               fio_tmp->Fclose();
+               delete fio_tmp;
+               temporary = true;
+               
+               // reopen the temporary file
+               fio->Fclose();
+               if(!fio->Fopen(_T("teledisk.$$$"), FILEIO_READ_BINARY)) {
+                       return false;
+               }
+       } else if(hdr.sig[0] == 'T' && hdr.sig[1] == 'D') {
+               // this image is not compressed
+       } else {
+               return false;
+       }
+       if(hdr.flag & 0x80) {
+               // skip comment
+               fio->Fread(&cmt, sizeof(td_cmt_t), 1);
+               fio->Fseek(cmt.len, FILEIO_SEEK_CUR);
+       }
+       
+       // create d88 header
+       d88_hdr_t d88_hdr;
+       d88_sct_t d88_sct;
+       
+       memset(&d88_hdr, 0, sizeof(d88_hdr_t));
+       my_strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "TELEDISK");
+       d88_hdr.protect = 0; // non-protected
+       
+       file_size.d = 0;
+       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));
+       
+       // create tracks
+       int trkcnt = 0, trkptr = sizeof(d88_hdr_t);
+       fio->Fread(&trk, sizeof(td_trk_t), 1);
+       while(trk.nsec != 0xff) {
+               d88_hdr.trkptr[trkcnt++] = trkptr;
+               if(hdr.sides == 1) {
+                       trkcnt++;
+               }
+               
+               // read sectors in this track
+               for(int i = 0; i < trk.nsec; i++) {
+                       uint8_t buf[2048], dst[2048];
+                       memset(buf, 0, sizeof(buf));
+                       memset(dst, 0, sizeof(dst));
+                       
+                       // read sector header
+                       fio->Fread(&sct, sizeof(td_sct_t), 1);
+                       
+                       // create d88 sector header
+                       memset(&d88_sct, 0, sizeof(d88_sct_t));
+                       d88_sct.c = sct.c;
+                       d88_sct.h = sct.h;
+                       d88_sct.r = sct.r;
+                       d88_sct.n = sct.n;
+                       d88_sct.nsec = trk.nsec;
+                       d88_sct.dens = (hdr.dens & 0x80) ? 0x40 : 0;
+                       d88_sct.del = (sct.ctrl & 4) ? 0x10 : 0;
+                       d88_sct.stat = (sct.ctrl & 2) ? 0xb0 : d88_sct.del;
+                       d88_sct.size = secsize[sct.n & 3];
+                       
+                       // create sector image
+                       if(sct.ctrl & 0x30) {
+                               d88_sct.stat = 0xf0; // data mark missing
+                               d88_sct.size = 0;
+                       } else {
+                               // read sector source
+                               int len = fio->Fgetc();
+                               len += fio->Fgetc() * 256 - 1;
+                               int flag = fio->Fgetc(), d = 0;
+                               fio->Fread(buf, len, 1);
+                               
+                               // convert
+                               if(flag == 0) {
+                                       memcpy(dst, buf, len);
+                               } else if(flag == 1) {
+                                       pair_t len2;
+                                       len2.read_2bytes_le_from(buf);
+                                       while(len2.sd--) {
+                                               dst[d++] = buf[2];
+                                               dst[d++] = buf[3];
+                                       }
+                               } else if(flag == 2) {
+                                       for(int s = 0; s < len;) {
+                                               int type = buf[s++];
+                                               int len2 = buf[s++];
+                                               if(type == 0) {
+                                                       while(len2--) {
+                                                               dst[d++] = buf[s++];
+                                                       }
+                                               } else if(type < 5) {
+                                                       uint8_t pat[256];
+                                                       int n = 2;
+                                                       while(type-- > 1) {
+                                                               n *= 2;
+                                                       }
+                                                       for(int j = 0; j < n; j++) {
+                                                               pat[j] = buf[s++];
+                                                       }
+                                                       while(len2--) {
+                                                               for(int j = 0; j < n; j++) {
+                                                                       dst[d++] = pat[j];
+                                                               }
+                                                       }
+                                               } else {
+                                                       break; // unknown type
+                                               }
+                                       }
+                               } else {
+                                       break; // unknown flag
+                               }
+                       }
+                       
+                       // copy to d88
+                       COPYBUFFER(&d88_sct, sizeof(d88_sct_t));
+                       COPYBUFFER(dst, d88_sct.size);
+                       trkptr += sizeof(d88_sct_t) + d88_sct.size;
+               }
+               // read next track
+               fio->Fread(&trk, sizeof(td_trk_t), 1);
+       }
+       d88_hdr.type = ((hdr.dens & 3) == 2) ? MEDIA_TYPE_2HD : ((trkcnt >> 1) > 60) ? MEDIA_TYPE_2DD : MEDIA_TYPE_2D;
+       d88_hdr.size = trkptr;
+       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));
+       
+       if(temporary) {
+               FILEIO::RemoveFile(_T("teledisk.$$$"));
+       }
+       return true;
+}
+
+// imagedisk image decoder (from MESS formats/imd_dsk.c by Mr.Miodrag Milanovic)
+
+bool DISK::imagedisk_to_d88(FILEIO *fio)
+{
+       int size = fio->FileLength();
+       fio->Fseek(0, FILEIO_SEEK_SET);
+       fio->Fread(tmp_buffer, size, 1);
+       
+       if(memcmp(tmp_buffer, "IMD ", 4) != 0) {
+               return false;
+       }
+       
+       int pos;
+       for(pos = 0; pos < size && tmp_buffer[pos] != 0x1a; pos++);
+       pos++;
+       
+       if(pos >= size) {
+               return false;
+       }
+       
+       // create d88 header
+       d88_hdr_t d88_hdr;
+       d88_sct_t d88_sct;
+       
+       memset(&d88_hdr, 0, sizeof(d88_hdr_t));
+       my_strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "IMAGEDISK");
+       d88_hdr.protect = 0; // non-protected
+       
+       file_size.d = 0;
+       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));
+       
+       // create tracks
+       int trkcnt = 0, trkptr = sizeof(d88_hdr_t);
+       int img_mode = -1;
+       uint8_t dst[8192];
+       
+       while(pos < size) {
+               // check track header
+               uint8_t mode = tmp_buffer[pos++];
+               uint8_t track = tmp_buffer[pos++];
+               uint8_t head = tmp_buffer[pos++];
+               uint8_t sector_count = tmp_buffer[pos++];
+               uint8_t ssize = tmp_buffer[pos++];
+               
+               if(sector_count == 0) {
+                       continue;
+               }
+               if(ssize == 0xff) {
+                       return false;
+               }
+               uint32_t actual_size = ssize < 7 ? 128 << ssize : 8192;
+               
+               // setup sector id
+               const uint8_t *snum = &tmp_buffer[pos];
+               pos += sector_count;
+               const uint8_t *tnum = head & 0x80 ? &tmp_buffer[pos] : NULL;
+               if(tnum)
+                       pos += sector_count;
+               const uint8_t *hnum = head & 0x40 ? &tmp_buffer[pos] : NULL;
+               if(hnum)
+                       pos += sector_count;
+               head &= 0x3f;
+               
+               // create new track
+               int trkside = track * 2 + (head & 1);
+               if(trkside < 164) {
+                       if(trkcnt < trkside) {
+                               trkcnt = trkside;
+                       }
+                       d88_hdr.trkptr[trkside] = trkptr;
+               }
+               if(img_mode == -1) {
+                       img_mode = mode & 3;
+               }
+               
+               // read sectors in this track
+               for(int i = 0; i < sector_count; i++) {
+                       // create d88 sector header
+                       uint8_t stype = tmp_buffer[pos++];
+                       memset(&d88_sct, 0, sizeof(d88_sct_t));
+                       d88_sct.c = tnum ? tnum[i] : track;
+                       d88_sct.h = hnum ? hnum[i] : head;
+                       d88_sct.r = snum[i];
+                       d88_sct.n = ssize;
+                       d88_sct.nsec = sector_count;
+                       d88_sct.dens = (mode < 3) ? 0x40 : 0;
+                       
+                       if(stype == 0 || stype > 8) {
+                               d88_sct.stat = 0xf0; // data mark missing
+                               d88_sct.size = 0;
+                       } else {
+                               d88_sct.del  = (stype == 3 || stype == 4 || stype == 7 || stype == 8) ? 0x10 : 0;
+                               d88_sct.stat = (stype == 5 || stype == 6 || stype == 7 || stype == 8) ? 0xb0 : d88_sct.del;
+                               d88_sct.size = actual_size;
+                               
+                               // create sector image
+                               if(stype == 2 || stype == 4 || stype == 6 || stype == 8) {
+                                       memset(dst, tmp_buffer[pos++], actual_size);
+                               } else {
+                                       memcpy(dst, &tmp_buffer[pos], actual_size);
+                                       pos += d88_sct.size;
+                               }
+                       }
+                       
+                       // copy to d88
+                       if(trkside < 164) {
+                               COPYBUFFER(&d88_sct, sizeof(d88_sct_t));
+                               COPYBUFFER(dst, d88_sct.size);
+                               trkptr += sizeof(d88_sct_t) + d88_sct.size;
+                       }
+               }
+       }
+       d88_hdr.type = (img_mode == 0) ? MEDIA_TYPE_2HD : (((trkcnt + 1) >> 1) > 60) ? MEDIA_TYPE_2DD : MEDIA_TYPE_2D;
+       d88_hdr.size = trkptr;
+       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));
+       return true;
+}
+
+// cpdread image decoder (from MESS formats/dsk_dsk.c by Mr.Olivier Galibert)
+
+#define DSK_FORMAT_HEADER      "MV - CPC"
+#define EXT_FORMAT_HEADER      "EXTENDED CPC DSK"
+
+#pragma pack(1)
+struct track_header {
+       uint8_t headertag[13];
+       uint16_t unused1;
+       uint8_t unused1b;
+       uint8_t track_number;
+       uint8_t side_number;
+       uint8_t datarate;
+       uint8_t rec_mode;
+       uint8_t sector_size_code;
+       uint8_t number_of_sector;
+       uint8_t gap3_length;
+       uint8_t filler_byte;
+};
+struct sector_header {
+       uint8_t track;
+       uint8_t side;
+       uint8_t sector_id;
+       uint8_t sector_size_code;
+       uint8_t fdc_status_reg1;
+       uint8_t fdc_status_reg2;
+       uint16_t data_length;
+};
+#pragma pack()
+
+bool DISK::cpdread_to_d88(FILEIO *fio)
+{
+       bool extendformat = false;
+       int image_size = fio->FileLength();
+       
+       fio->Fseek(0, FILEIO_SEEK_SET);
+       fio->Fread(tmp_buffer, image_size, 1);
+       
+       if(memcmp(tmp_buffer, EXT_FORMAT_HEADER, 16) == 0) {
+               extendformat = true;
+       } else if(memcmp(tmp_buffer, DSK_FORMAT_HEADER, 8) == 0) {
+               extendformat = false;
+       } else {
+               return false;
+       }
+       
+       int heads = tmp_buffer[0x31];
+       int skip = 1;
+       if(heads == 1) {
+               skip = 2;
+       }
+       int tracks = tmp_buffer[0x30];
+       int track_offsets[84 * 2];
+       bool track_offsets_error = false;
+       if(!extendformat) {
+               int cnt = 0, tmp = 0x100;
+               for(int i = 0; i < tracks * heads; i++) {
+                       track_offsets_error = (memcmp(tmp_buffer + tmp, "Track-Info", 10) != 0);
+                       if(track_offsets_error) {
+                               break;
+                       }
+                       track_offsets[cnt] = tmp;
+                       tmp += tmp_buffer[0x32] + tmp_buffer[0x33] * 256;
+                       cnt += skip;
+               }
+       } else  {
+               int cnt = 0, tmp = 0x100;
+               for(int i = 0; i < tracks * heads; i++) {
+                       int length = tmp_buffer[0x34 + i] << 8;
+                       if(length != 0) {
+                               track_offsets_error = (memcmp(tmp_buffer + tmp, "Track-Info", 10) != 0);
+                               if(track_offsets_error) {
+                                       break;
+                               }
+                               track_offsets[cnt] = tmp;
+                               tmp += length;
+                       } else {
+                               track_offsets[cnt] = image_size;
+                       }
+                       cnt += skip;
+               }
+       }
+       if(track_offsets_error) {
+               // I found the dsk image that the track size in table is 1100h, but the actual track size is 900h,
+               // so I modified this code to search "Track-Info" at the top of track information block
+               int cnt = 0, tmp = 0x100;
+               for(int i = 0; i < tracks * heads; i++) {
+                       bool found = false;
+                       for(; tmp < image_size; tmp += 0x10) {
+                               found = (memcmp(tmp_buffer + tmp, "Track-Info", 10) == 0);
+                               if(found) {
+                                       break;
+                               }
+                       }
+                       if(found) {
+                               track_offsets[cnt] = tmp;
+                               tmp += 0x10;
+                       } else {
+                               track_offsets[cnt] = image_size;
+                       }
+                       cnt += skip;
+               }
+       }
+       
+       // create d88 header
+       d88_hdr_t d88_hdr;
+       d88_sct_t d88_sct;
+       
+       memset(&d88_hdr, 0, sizeof(d88_hdr_t));
+       my_strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "CPDREAD");
+       d88_hdr.protect = 0; // non-protected
+       
+       file_size.d = 0;
+       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));
+       
+       // create tracks
+       int total = 0, trkptr = sizeof(d88_hdr_t);
+       
+       for(int track = 0; track < tracks; track++) {
+               for(int side = 0; side < heads; side++) {
+                       if(track_offsets[(track << 1) + side] >= image_size) {
+                               continue;
+                       }
+                       if((track << 1) + side < 164) {
+                               d88_hdr.trkptr[(track << 1) + side] = trkptr;
+                       }
+                       
+                       track_header tr;
+                       memcpy(&tr, tmp_buffer + track_offsets[(track << 1) + side], sizeof(tr));
+                       int pos = track_offsets[(track << 1) + side] + 0x100;
+                       for(int j = 0; j < tr.number_of_sector; j++) {
+                               sector_header sector;
+                               memcpy(&sector, tmp_buffer + track_offsets[(track << 1) + side] + sizeof(tr) + (sizeof(sector) * j), sizeof(sector));
+                               
+                               // create d88 sector header
+                               memset(&d88_sct, 0, sizeof(d88_sct_t));
+                               d88_sct.c = sector.track;
+                               d88_sct.h = sector.side;
+                               d88_sct.r = sector.sector_id;
+                               d88_sct.n = sector.sector_size_code;
+                               d88_sct.nsec = tr.number_of_sector;
+                               if(extendformat) {
+                                       d88_sct.size = sector.data_length;
+                                       d88_sct.dens = (tr.rec_mode == 1) ? 0x40 : 0;
+                               } else {
+                                       d88_sct.size = 128 << tr.sector_size_code;
+                                       d88_sct.dens = (tr.sector_size_code == 0) ? 0x40 : 0; // FIXME
+                               }
+                               d88_sct.del = (sector.fdc_status_reg1 == 0xb2) ? 0x10 : 0;
+                               d88_sct.stat = (d88_sct.size == 0) ? 0xf0 : (sector.fdc_status_reg1 == 0xb5) ? 0xb0 : d88_sct.del;
+                               
+                               // copy to d88
+                               if((track << 1) + side < 164) {
+                                       COPYBUFFER(&d88_sct, sizeof(d88_sct_t));
+                                       COPYBUFFER(tmp_buffer + pos, d88_sct.size);
+                                       trkptr += sizeof(d88_sct_t) + d88_sct.size;
+                               }
+                               total += d88_sct.size;
+                               
+                               if(extendformat) {
+                                       pos += sector.data_length;
+                               } else {
+                                       pos += 128 << tr.sector_size_code;
+                               }
+                       }
+               }
+       }
+       d88_hdr.type = (total < (368640 + 655360) / 2) ? MEDIA_TYPE_2D : (total < (737280 + 1228800) / 2) ? MEDIA_TYPE_2DD : MEDIA_TYPE_2HD;
+       d88_hdr.size = trkptr;
+       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));
+       return true;
+}
+
+// nfd r0/r1 image decoder
+
+bool DISK::nfdr0_to_d88(FILEIO *fio)
+{
+       // from NFD r0\8c`\8e®\83t\83@\83C\83\8b\8d\\91¢\8ed\97l 2001/01/22 LED
+       typedef struct {
+           BYTE  C;                            // C \81i0xFF\82Ì\8e\9e\83Z\83N\83^\96³\82µ\81j
+           BYTE  H;                            // H
+           BYTE  R;                            // R
+           BYTE  N;                            // N
+           BYTE  flMFM;                        // 0:FM / 1:MFM
+           BYTE  flDDAM;                       // 0:DAM / 1:DDAM
+           BYTE  byStatus;                     // READ DATA(FDDBIOS)\82Ì\8c\8b\89Ê
+           BYTE  byST0;                        // READ DATA(FDDBIOS)\82Ì\8c\8b\89Ê ST0
+           BYTE  byST1;                        // READ DATA(FDDBIOS)\82Ì\8c\8b\89Ê ST1
+           BYTE  byST2;                        // READ DATA(FDDBIOS)\82Ì\8c\8b\89Ê ST2
+           BYTE  byPDA;                        // FDDBIOS\82Å\8eg\97p\82·\82é\83A\83h\83\8c\83X
+           char Reserve1[5];                   // \97\\96ñ
+       }NFD_SECT_ID,*LP_NFD_SECT_ID;
+       
+       typedef struct {
+           char  szFileID[15];                 // \8e¯\95ÊID "T98FDDIMAGE.R0"
+           char  Reserve1[1];                  // \97\\96ñ
+           char  szComment[0x100];             // \83C\83\81\81[\83W\83R\83\81\83\93\83g(ASCIIz)
+           DWORD dwHeadSize;                   // \83w\83b\83_\95\94\82Ì\83T\83C\83Y
+           BYTE  flProtect;                    // 0\88È\8aO : \83\89\83C\83g\83v\83\8d\83e\83N\83g
+           BYTE  byHead;                       // \83w\83b\83h\90\94
+           char  Reserve2[10];                 // \97\\96ñ
+           NFD_SECT_ID si[163][26];            // \83Z\83N\83^ID(\8cã\8fq)
+           char  Reserve3[0x10];               // \97\\96ñ
+       }NFD_FILE_HEAD,*LP_NFD_FILE_HEAD;
+       
+       // check nfd header
+       NFD_FILE_HEAD head;
+       
+       fio->Fseek(0, FILEIO_SEEK_SET);
+       fio->Fread(&head, sizeof(head), 1);
+       
+       if(strncmp(head.szFileID, "T98FDDIMAGE.R0", 14) != 0) {
+               return false;
+       }
+       fio->Fseek(head.dwHeadSize, FILEIO_SEEK_SET);
+       
+       // create d88 header
+       d88_hdr_t d88_hdr;
+       d88_sct_t d88_sct;
+       
+       memset(&d88_hdr, 0, sizeof(d88_hdr_t));
+       my_strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "NFD R0");
+       d88_hdr.protect = head.flProtect;
+       
+       file_size.d = 0;
+       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));
+       
+       // create tracks
+       int trkptr = sizeof(d88_hdr_t);
+       
+       for(int c = 0; c < 163; c++) {
+               int nsec = 0;
+               
+               for(int s = 0; s < 26; s++) {
+                       if(head.si[c][s].C != 0xff) {
+                               nsec++;
+                       }
+               }
+               if(nsec) {
+                       d88_hdr.trkptr[c] = trkptr;
+                       
+                       // read sectors in this track
+                       for(int s = 0; s < 26; s++) {
+                               if(head.si[c][s].C != 0xff) {
+                                       // create d88 sector header
+                                       memset(&d88_sct, 0, sizeof(d88_sct_t));
+                                       d88_sct.c = head.si[c][s].C;
+                                       d88_sct.h = head.si[c][s].H;
+                                       d88_sct.r = head.si[c][s].R;
+                                       d88_sct.n = head.si[c][s].N;
+                                       d88_sct.nsec = nsec;
+                                       d88_sct.dens = head.si[c][s].flMFM ? 0 : 0x40;
+                                       d88_sct.del = head.si[c][s].flDDAM ? 0x10 : 0;
+                                       if(head.si[c][s].byST1 & 0x20) {
+                                               if(head.si[c][s].byST2 & 0x20) {
+                                                       d88_sct.stat = 0xb0; // data crc error
+                                               } else {
+                                                       d88_sct.stat = 0xa0; // id crc error
+                                               }
+                                       } else if(head.si[c][s].byST1 & 0x01) {
+                                               d88_sct.stat = 0xe0; // address mark missing
+                                       } else if(head.si[c][s].byST2 & 0x01) {
+                                               d88_sct.stat = 0xf0; // data mark missing
+                                       } else {
+                                               d88_sct.stat = d88_sct.del;
+                                       }
+                                       d88_sct.size = secsize[d88_sct.n & 3];
+                                       
+                                       // create sector image
+                                       uint8_t dst[16384];
+                                       memset(dst, 0xe5, sizeof(dst));
+                                       fio->Fread(dst, d88_sct.size, 1);
+                                       
+                                       // copy to d88
+                                       COPYBUFFER(&d88_sct, sizeof(d88_sct_t));
+                                       COPYBUFFER(dst, d88_sct.size);
+                                       trkptr += sizeof(d88_sct_t) + d88_sct.size;
+                                       
+                                       if(head.si[c][s].byPDA == 0x10) {
+                                               d88_hdr.type = MEDIA_TYPE_2DD;
+                                       } else {
+                                               d88_hdr.type = MEDIA_TYPE_2HD;
+                                       }
+                               }
+                       }
+               }
+       }
+       d88_hdr.size = trkptr;
+       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));
+       return true;
+}
+
+bool DISK::nfdr1_to_d88(FILEIO *fio)
+{
+       // from NFD r1\8c`\8e®\83t\83@\83C\83\8b\8d\\91¢\8ed\97l 2001/09/14 LED
+       typedef struct {
+//         char szFileID[sizeof(NFD_FILE_ID1)];        /* \8e¯\95ÊID "T98FDDIMAGE.R1"  */
+//         char Reserv1[0x10-sizeof(NFD_FILE_ID1)];    /* \97\\94õ                     */
+           char  szFileID[15];                         /* \8e¯\95ÊID "T98FDDIMAGE.R1"  */
+           char  Reserve1[1];                          /* \97\\96ñ                     */
+           char szComment[0x100];                      /* \83R\83\81\83\93\83g                 */
+           DWORD dwHeadSize;                           /* \83w\83b\83_\82Ì\83T\83C\83Y           */
+           BYTE flProtect;                             /* \83\89\83C\83g\83v\83\8d\83e\83N\83g0\88È\8aO    */
+           BYTE byHead;                                /* \83w\83b\83h\90\94 1-2             */
+           char Reserv2[0x10-4-1-1];                   /* \97\\94õ                     */
+           DWORD dwTrackHead[164];                     /* \83g\83\89\83b\83NID\88Ê\92u           */
+           DWORD dwAddInfo;                            /* \92Ç\89Á\8fî\95ñ\83w\83b\83_\82Ì\83A\83h\83\8c\83X */
+           char Reserv3[0x10-4];                       /* \97\\94õ                     */
+       }NFD_FILE_HEAD1,*LP_NFD_FILE_HEAD1;
+       
+       typedef struct {
+           WORD wSector;                               /* \83Z\83N\83^ID\90\94               */
+           WORD wDiag;                                 /* \93Á\8eê ID\90\94                */
+           char Reserv1[0x10-4];                       /* \97\\94õ                     */
+       }NFD_TRACK_ID1,*LP_NFD_TRACK_ID1;
+       
+       typedef struct {
+           BYTE    C;                                  /* C                        */
+           BYTE    H;                                  /* H                        */
+           BYTE    R;                                  /* R                        */
+           BYTE    N;                                  /* N                        */
+           BYTE    flMFM;                              /* MFM(1)/FM(0)             */
+           BYTE    flDDAM;                             /* DDAM(1)/DAM(0)           */
+           BYTE    byStatus;                           /* READ DATA RESULT         */
+           BYTE    bySTS0;                             /* ST0                      */
+           BYTE    bySTS1;                             /* ST1                      */
+           BYTE    bySTS2;                             /* ST2                      */
+           BYTE    byRetry;                            /* RetryData\82È\82µ(0)\82 \82è(1-) */
+           BYTE    byPDA;                              /* PDA                      */
+           char Reserv1[0x10-12];                      /* \97\\94õ                     */
+       }NFD_SECT_ID1,*LP_NFD_SECT_ID1;
+       
+       typedef struct {
+           BYTE    Cmd;                                /* Command                  */
+           BYTE    C;                                  /* C                        */
+           BYTE    H;                                  /* H                        */
+           BYTE    R;                                  /* R                        */
+           BYTE    N;                                  /* N                        */
+           BYTE    byStatus;                           /* READ DATA RESULT         */
+           BYTE    bySTS0;                             /* ST0                      */
+           BYTE    bySTS1;                             /* ST1                      */
+           BYTE    bySTS2;                             /* ST2                      */
+           BYTE    byRetry;                            /* RetryData\82È\82µ(0)\82 \82è(1-) */
+           DWORD   dwDataLen;
+           BYTE    byPDA;                              /* PDA                      */
+           char Reserv1[0x10-15];                      /* \97\\94õ                     */
+       }NFD_DIAG_ID1,*LP_NFD_DIAG_ID1;
+       
+       // check nfd header
+       NFD_FILE_HEAD1 head;
+       
+       fio->Fseek(0, FILEIO_SEEK_SET);
+       fio->Fread(&head, sizeof(head), 1);
+       
+       if(strncmp(head.szFileID, "T98FDDIMAGE.R1", 14) != 0) {
+               return false;
+       }
+       fio->Fseek(head.dwHeadSize, FILEIO_SEEK_SET);
+       
+       // not implemented yet :-(
+       return false;
+}
+
+// solid image decoder
+
+bool DISK::solid_to_d88(FILEIO *fio, int type, int ncyl, int nside, int nsec, int size, bool mfm)
+{
+       int n = 0, t = 0;
+       
+       media_type = type;
+       solid_ncyl = ncyl;
+       solid_nside = nside;
+       solid_nsec = nsec;
+       solid_size = size;
+       solid_mfm = mfm;
+       
+       // create d88 header
+       d88_hdr_t d88_hdr;
+       d88_sct_t d88_sct;
+       
+       memset(&d88_hdr, 0, sizeof(d88_hdr_t));
+       my_strcpy_s(d88_hdr.title, sizeof(d88_hdr.title), "SOLID");
+       d88_hdr.protect = 0; // non-protected
+       
+       file_size.d = 0;
+       COPYBUFFER(&d88_hdr, sizeof(d88_hdr_t));
+       
+       // sector length
+       for(int i = 0; i < 8; i++) {
+               if(size == (128 << i)) {
+                       n = i;
+                       break;
+               }
+       }
+       
+       // create tracks
+       int trkptr = sizeof(d88_hdr_t);
+       
+       for(int c = 0; c < ncyl; c++) {
+               for(int h = 0; h < nside; h++) {
+                       d88_hdr.trkptr[t++] = trkptr;
+                       if(nside == 1) {
+                               t++;
+                       }
+                       
+                       // read sectors in this track
+                       for(int s = 0; s < nsec; s++) {
+                               // create d88 sector header
+                               memset(&d88_sct, 0, sizeof(d88_sct_t));
+                               d88_sct.c = c;
+                               d88_sct.h = h;
+                               d88_sct.r = s + 1;
+                               d88_sct.n = n;
+                               d88_sct.nsec = nsec;
+                               d88_sct.dens = 0;
+                               d88_sct.del = 0;
+                               d88_sct.stat = 0;
+                               d88_sct.size = size;
+                               
+                               // create sector image
+                               uint8_t dst[16384];
+                               memset(dst, 0xe5, sizeof(dst));
+                               fio->Fread(dst, size, 1);
+                               
+                               // copy to d88
+                               COPYBUFFER(&d88_sct, sizeof(d88_sct_t));
+                               COPYBUFFER(dst, size);
+                               trkptr += sizeof(d88_sct_t) + size;
+                       }
+               }
+       }
+       d88_hdr.type = (type == MEDIA_TYPE_144) ? MEDIA_TYPE_2HD : type;
+       d88_hdr.size = trkptr;
+       memcpy(buffer, &d88_hdr, sizeof(d88_hdr_t));
+       return true;
+}
+
+#define STATE_VERSION  14
+
+#include "../../statesub.h"
+#include "../../qt/gui/csp_logger.h"
+
+void DISK::decl_state(void *p)
+{
+       CSP_Logger *p_logger = (CSP_Logger *)p;
+       state_entry = new csp_state_utils(STATE_VERSION, drive_num, _T("FLOPPY_DISK"), p_logger);
+
+       DECL_STATE_ENTRY_1D_ARRAY(buffer, sizeof(buffer));
+       DECL_STATE_ENTRY_STRING(orig_path, sizeof(orig_path) / sizeof(_TCHAR));
+       DECL_STATE_ENTRY_STRING(dest_path, sizeof(dest_path) / sizeof(_TCHAR));
+       
+       DECL_STATE_ENTRY_PAIR(file_size);
+       DECL_STATE_ENTRY_INT32(file_bank);
+       DECL_STATE_ENTRY_UINT32(orig_file_size);
+       DECL_STATE_ENTRY_UINT32(orig_crc32);
+       DECL_STATE_ENTRY_BOOL(trim_required);
+       DECL_STATE_ENTRY_BOOL(is_1dd_image);
+       DECL_STATE_ENTRY_BOOL(is_solid_image);
+       DECL_STATE_ENTRY_BOOL(is_fdi_image);
+       
+       DECL_STATE_ENTRY_1D_ARRAY(fdi_header, sizeof(fdi_header));
+       
+       DECL_STATE_ENTRY_INT32(solid_ncyl);
+       DECL_STATE_ENTRY_INT32(solid_nside);
+       DECL_STATE_ENTRY_INT32(solid_nsec);
+       DECL_STATE_ENTRY_INT32(solid_size);
+       DECL_STATE_ENTRY_BOOL(solid_mfm);
+       DECL_STATE_ENTRY_BOOL(inserted);
+       DECL_STATE_ENTRY_BOOL(ejected);
+       DECL_STATE_ENTRY_BOOL(write_protected);
+       DECL_STATE_ENTRY_BOOL(changed);
+       DECL_STATE_ENTRY_UINT8(media_type);
+       DECL_STATE_ENTRY_INT32(is_special_disk);
+       
+       DECL_STATE_ENTRY_1D_ARRAY(track, sizeof(track));
+                                                         
+       DECL_STATE_ENTRY_PAIR(sector_num);
+       DECL_STATE_ENTRY_BOOL(track_mfm);
+       DECL_STATE_ENTRY_BOOL(invalid_format);
+//     DECL_STATE_ENTRY_BOOL(no_skew);
+       DECL_STATE_ENTRY_1D_ARRAY(sync_position, sizeof(sync_position) / sizeof(int));
+       DECL_STATE_ENTRY_1D_ARRAY(am1_position, sizeof(am1_position) / sizeof(int));
+       DECL_STATE_ENTRY_1D_ARRAY(id_position, sizeof(id_position) / sizeof(int));
+       DECL_STATE_ENTRY_1D_ARRAY(data_position, sizeof(data_position) / sizeof(int));
+//     DECL_STATE_ENTRY_INT32(gap3_size);
+       DECL_STATE_ENTRY_INT32(_tmp_sector_offset);
+       DECL_STATE_ENTRY_PAIR(sector_size);
+       
+       DECL_STATE_ENTRY_1D_ARRAY(id, sizeof(id));
+       DECL_STATE_ENTRY_UINT8(density);
+       DECL_STATE_ENTRY_BOOL(deleted);
+       DECL_STATE_ENTRY_BOOL(addr_crc_error);
+       DECL_STATE_ENTRY_BOOL(data_crc_error);
+       DECL_STATE_ENTRY_UINT8(drive_type);
+       DECL_STATE_ENTRY_INT32(drive_rpm);
+       DECL_STATE_ENTRY_BOOL(drive_mfm);
+}
+void DISK::save_state(FILEIO* state_fio)
+{
+       _tmp_sector_offset = (sector != NULL) ? (int)(sector - buffer) : -1;
+       
+       if(state_entry != NULL) {
+               state_entry->save_state(state_fio);
+       }
+       //state_fio->FputUint32(STATE_VERSION);
+       
+       //state_fio->Fwrite(buffer, sizeof(buffer), 1);
+       //state_fio->Fwrite(orig_path, sizeof(orig_path), 1);
+       //state_fio->Fwrite(dest_path, sizeof(dest_path), 1);
+       //state_fio->FputUint32(file_size.d);
+       //state_fio->FputInt32(file_bank);
+       //state_fio->FputUint32(orig_file_size);
+       //state_fio->FputUint32(orig_crc32);
+       //state_fio->FputBool(trim_required);
+       //state_fio->FputBool(is_1dd_image);
+       //state_fio->FputBool(is_solid_image);
+       //state_fio->FputBool(is_fdi_image);
+       //state_fio->Fwrite(fdi_header, sizeof(fdi_header), 1);
+       //state_fio->FputInt32(solid_ncyl);
+       //state_fio->FputInt32(solid_nside);
+       //state_fio->FputInt32(solid_nsec);
+       //state_fio->FputInt32(solid_size);
+       //state_fio->FputBool(solid_mfm);
+       //state_fio->FputBool(inserted);
+       //state_fio->FputBool(ejected);
+       //state_fio->FputBool(write_protected);
+       //state_fio->FputBool(changed);
+       //state_fio->FputUint8(media_type);
+       //state_fio->FputInt32(is_special_disk);
+       //state_fio->Fwrite(track, sizeof(track), 1);
+       //state_fio->FputInt32(sector_num.sd);
+       //state_fio->FputBool(track_mfm);
+       //state_fio->FputBool(invalid_format);
+//     state_fio->FputBool(no_skew);
+       //state_fio->Fwrite(sync_position, sizeof(sync_position), 1);
+       //state_fio->Fwrite(am1_position, sizeof(am1_position), 1);
+       //state_fio->Fwrite(id_position, sizeof(id_position), 1);
+       //state_fio->Fwrite(data_position, sizeof(data_position), 1);
+//     state_fio->FputInt32(gap3_size);
+       //state_fio->FputInt32(sector ? (int)(sector - buffer) : -1);
+       //state_fio->FputInt32(sector_size.sd);
+       //state_fio->Fwrite(id, sizeof(id), 1);
+       //state_fio->FputUint8(density);
+       //state_fio->FputBool(deleted);
+       //state_fio->FputBool(addr_crc_error);
+       //state_fio->FputBool(data_crc_error);
+       //state_fio->FputUint8(drive_type);
+       //state_fio->FputInt32(drive_rpm);
+       //state_fio->FputBool(drive_mfm);
+}
+
+bool DISK::load_state(FILEIO* state_fio)
+{
+       bool mb = false;
+       if(state_entry != NULL) {
+               mb = state_entry->load_state(state_fio);
+       }
+       if(!mb) return false;
+       //if(state_fio->FgetUint32() != STATE_VERSION) {
+       //      return false;
+       //}
+       //state_fio->Fread(buffer, sizeof(buffer), 1);
+       //state_fio->Fread(orig_path, sizeof(orig_path), 1);
+       //state_fio->Fread(dest_path, sizeof(dest_path), 1);
+       //file_size.d = state_fio->FgetUint32();
+       //file_bank = state_fio->FgetInt32();
+       //orig_file_size = state_fio->FgetUint32();
+       //orig_crc32 = state_fio->FgetUint32();
+       //trim_required = state_fio->FgetBool();
+       //is_1dd_image = state_fio->FgetBool();
+       //is_solid_image = state_fio->FgetBool();
+       //is_fdi_image = state_fio->FgetBool();
+       //state_fio->Fread(fdi_header, sizeof(fdi_header), 1);
+       //solid_ncyl = state_fio->FgetInt32();
+       //solid_nside = state_fio->FgetInt32();
+       //solid_nsec = state_fio->FgetInt32();
+       //solid_size = state_fio->FgetInt32();
+       //solid_mfm = state_fio->FgetBool();
+       //inserted = state_fio->FgetBool();
+       //ejected = state_fio->FgetBool();
+       //write_protected = state_fio->FgetBool();
+       //changed = state_fio->FgetBool();
+       //media_type = state_fio->FgetUint8();
+       //is_special_disk = state_fio->FgetInt32();
+       //state_fio->Fread(track, sizeof(track), 1);
+       //sector_num.sd = state_fio->FgetInt32();
+       //track_mfm = state_fio->FgetBool();
+       //invalid_format = state_fio->FgetBool();
+//     no_skew = state_fio->FgetBool();
+       //state_fio->Fread(sync_position, sizeof(sync_position), 1);
+       //state_fio->Fread(am1_position, sizeof(am1_position), 1);
+       //state_fio->Fread(id_position, sizeof(id_position), 1);
+       //state_fio->Fread(data_position, sizeof(data_position), 1);
+//     gap3_size = state_fio->FgetInt32();
+       //int offset = state_fio->FgetInt32();
+       //sector = (offset != -1) ? buffer + offset : NULL;
+       //sector_size.sd = state_fio->FgetInt32();
+       //state_fio->Fread(id, sizeof(id), 1);
+       //density = state_fio->FgetUint8();
+       //deleted = state_fio->FgetBool();
+       //addr_crc_error = state_fio->FgetBool();
+       //data_crc_error = state_fio->FgetBool();
+       //drive_type = state_fio->FgetUint8();
+       //drive_rpm = state_fio->FgetInt32();
+       //drive_mfm = state_fio->FgetBool();
+       sector = (_tmp_sector_offset != -1) ? (buffer + _tmp_sector_offset) : NULL;
+       return true;
+}
+