Subversion Repositories pentevo

Rev

Rev 798 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed | ?url?

  1. #include "std.h"
  2.  
  3. #include "emul.h"
  4. #include "vars.h"
  5. #include "fdd.h"
  6. #include "util.h"
  7.  
  8. // ┬ёх фрээ√х тэєЄЁш ipf Їрщыр т ЇюЁьрЄх big endian
  9.  
  10. static constexpr size_t CAPS_MAXPLATFORM = 4;
  11.  
  12. static constexpr char ID_CAPS[] = "CAPS";
  13. static constexpr char ID_INFO[] = "INFO";
  14. static constexpr char ID_IMGE[] = "IMGE";
  15. static constexpr char ID_DATA[] = "DATA";
  16.  
  17. static constexpr u16 MFM_MARK_A1 = 0x4489;
  18. static constexpr u16 MFM_MARK_C2 = 0x5224;
  19.  
  20. static inline u32 be2cpu(u32 x)
  21. {
  22.     return _byteswap_ulong(x);
  23. }
  24.  
  25. static inline u16 be2cpu(u16 x)
  26. {
  27.     return _byteswap_ushort(x);
  28. }
  29.  
  30. static inline u8 ReadU8(u8 *&Ptr)
  31. {
  32.     u8 x = *Ptr++;
  33.     return x;
  34. }
  35.  
  36. static inline u16 ReadU16(u8 *&Ptr)
  37. {
  38.     u16 x = *((u16 *)Ptr);
  39.     Ptr += sizeof(u16);
  40.     return x;
  41. }
  42.  
  43. static inline u32 ReadU32(u8 *&Ptr)
  44. {
  45.     u32 x = *((u32 *)Ptr);
  46.     Ptr += sizeof(u32);
  47.     return x;
  48. }
  49.  
  50. #pragma pack(push, 1)
  51. struct TCapsId
  52. {
  53.     char Name[4];
  54.     u32 Size;
  55.     u32 Crc;
  56. };
  57.  
  58. enum TEncoderType : u32
  59. {
  60.     CAPS_ENCODER = 1,
  61.     SPS_ENCODER = 2
  62. };
  63.  
  64. // caps packed date.time format
  65. struct TCapsDateTime
  66. {
  67.     u32 date; // packed date, yyyymmdd
  68.     u32 time; // packed time, hhmmssttt
  69. };
  70.  
  71. // platform IDs, not about configuration, but intended use
  72. enum TPlatformId : u32
  73. {
  74.     cppidNA = 0, // invalid platform (dummy entry)
  75.     cppidAmiga,
  76.     cppidAtariST,
  77.     cppidPC,
  78.     cppidAmstradCPC,
  79.     cppidSpectrum,
  80.     cppidSamCoupe,
  81.     cppidArchimedes,
  82.     cppidC64,
  83.     cppidAtari8,
  84.     cppidLast
  85. };
  86.  
  87.  
  88. // image information
  89. struct TCapsInfo
  90. {
  91.     u32 type;        // image type
  92.     TEncoderType encoder;     // image encoder ID
  93.     u32 encrev;      // image encoder revision
  94.     u32 release;     // release ID
  95.     u32 revision;    // release revision ID
  96.     u32 origin;      // original source reference
  97.     u32 mincylinder; // lowest cylinder number
  98.     u32 maxcylinder; // highest cylinder number
  99.     u32 minhead;     // lowest head number
  100.     u32 maxhead;     // highest head number
  101.     TCapsDateTime crdt;  // image creation date.time
  102.     TPlatformId platform[CAPS_MAXPLATFORM]; // intended platform(s)
  103.     u32 disknum;     // disk# for release, >= 1 if multidisk
  104.     u32 userid;      // user id of the image creator
  105.     u32 reserved[3]; // future use
  106. };
  107.  
  108. // density types
  109. enum TDensityType : u32
  110. {
  111.     cpdenNA = 0,     // invalid density
  112.     cpdenNoise,    // cells are unformatted (random size)
  113.     cpdenAuto,     // automatic cell size, according to track size
  114.     cpdenCLAmiga,  // Copylock Amiga
  115.     cpdenCLAmiga2, // Copylock Amiga, new
  116.     cpdenCLST,     // Copylock ST
  117.     cpdenSLAmiga,  // Speedlock Amiga
  118.     cpdenSLAmiga2, // Speedlock Amiga, old
  119.     cpdenABAmiga,  // Adam Brierley Amiga
  120.     cpdenABAmiga2, // Adam Brierley, density key Amiga
  121.     cpdenLast
  122. };
  123.  
  124. // signal processing used
  125. enum TSigType : u32
  126. {
  127.     cpsigNA = 0, // invalid signal type
  128.     cpsig2us,  // 2us cells
  129.     cpsigLast
  130. };
  131.  
  132.  
  133. // track image descriptor
  134. struct TCapsImage
  135. {
  136.     u32 cylinder; // cylinder#
  137.     u32 head;     // head#
  138.     TDensityType dentype;  // density type
  139.     TSigType sigtype;  // signal processing type
  140.     u32 trksize;  // decoded track size, rounded
  141.     u32 startpos; // start position, rounded
  142.     u32 startbit; // start position on original data
  143.     u32 databits; // decoded data size in bits
  144.     u32 gapbits;  // decoded gap size in bits
  145.     u32 trkbits;  // decoded track size in bits
  146.     u32 blkcnt;   // number of blocks
  147.     u32 process;  // encoder prcocess
  148.     u32 flag;     // image flags
  149.     u32 did;      // data chunk identifier
  150.     u32 reserved[3]; // future use
  151. };
  152.  
  153. // data area
  154. struct TCapsData
  155. {
  156.     u32 size;  // data area size in bytes after chunk
  157.     u32 bsize; // data area size in bits
  158.     u32 dcrc;  // data area crc
  159.     u32 did;   // data chunk identifier
  160. };
  161.  
  162. // original meaning of some CapsBlock entries for old images
  163. struct TCapsBlockExt
  164. {
  165.     u32 blocksize;  // decoded block size, rounded
  166.     u32 gapsize;    // decoded gap size, rounded
  167. };
  168.  
  169. // new meaning of some CapsBlock entries for new images
  170. struct TSPSBlockExt
  171. {
  172.     u32 gapoffset;  // offset of gap stream in data area
  173.     u32 celltype;   // bitcell type
  174. };
  175.  
  176. // union for old or new images
  177. union TCapsBlockType
  178. {
  179.     TCapsBlockExt caps; // access old image
  180.     TSPSBlockExt sps;   // access new image
  181.     static_assert(sizeof(sps) == sizeof(caps),"static_assert");
  182. };
  183.  
  184. // encoder types
  185. enum TEncType : u32
  186. {
  187.     cpencNA = 0, // invalid encoder
  188.     cpencMFM,  // MFM
  189.     cpencRaw,  // no encoder used, test data only
  190.     cpencLast
  191. };
  192.  
  193. // CapsBlock flags
  194. union TCapsBlockFlags // ╧юЁ фюъ сшЄют√ї яюыхщ т ёЄЁєъЄєЁх ёююЄтхЄёЄтєхЄ little endian
  195. {
  196.     struct
  197.     {
  198.         u32 CAPS_BF_GP0 : 1; // bit0
  199.         u32 CAPS_BF_GP1 : 1; // bit1
  200.         u32 CAPS_BF_DMB : 1; // bit2
  201.         u32 : 29;
  202.     };
  203.     u32 Flags;
  204. };
  205.  
  206. // block image descriptor
  207. struct TCapsBlock
  208. {
  209.     u32 blockbits;  // decoded block size in bits
  210.     u32 gapbits;    // decoded gap size in bits
  211.     TCapsBlockType bt;  // content depending on image type
  212.     TEncType enctype;    // encoder type
  213.     TCapsBlockFlags flag;       // block flags
  214.     u32 gapvalue;   // default gap value
  215.     u32 dataoffset; // offset of data stream in data area (юЄэюёшЄхы№эю яхЁтюую caps сыюър)
  216. };
  217.  
  218. // gap types
  219. enum TGapType : u8
  220. {
  221.     cpgapEnd = 0, // gap stream end
  222.     cpgapCount, // gap counter
  223.     cpgapData,  // gap data pattern
  224.     cpgapLast
  225. };
  226.  
  227. union TGapStreamElementHdr
  228. {
  229.     struct
  230.     {
  231.         TGapType GapType : 5;
  232.         u8 GapSizeWidth : 3;
  233.     };
  234.     u8 Hdr;
  235. };
  236.  
  237. // data types
  238. enum TDataType : u8
  239. {
  240.     cpdatEnd = 0, // data stream end
  241.     cpdatMark,  // mark/sync
  242.     cpdatData,  // data
  243.     cpdatGap,   // gap
  244.     cpdatRaw,   // raw
  245.     cpdatFData, // flakey data
  246.     cpdatLast
  247. };
  248.  
  249. union TDataStreamElementHdr
  250. {
  251.     struct
  252.     {
  253.         TDataType DataType : 5;
  254.         u8 DataSizeWidth : 3;
  255.     };
  256.     u8 Hdr;
  257. };
  258. #pragma pack(pop)
  259.  
  260. static void ReadCapsId(TCapsId *CapsId, u8 *&Ptr)
  261. {
  262.     memcpy(CapsId->Name, Ptr, 4); Ptr += 4;
  263.     CapsId->Size = be2cpu(ReadU32(Ptr));
  264.     CapsId->Crc = be2cpu(ReadU32(Ptr));
  265. }
  266.  
  267. static void ReadCapsDateTime(TCapsDateTime *CapsDateTime, u8 *&Ptr)
  268. {
  269.     CapsDateTime->date = be2cpu(ReadU32(Ptr));
  270.     CapsDateTime->time = be2cpu(ReadU32(Ptr));
  271. }
  272.  
  273. static void ReadCapsInfo(TCapsInfo *CapsInfo, u8 *&Ptr)
  274. {
  275.     CapsInfo->type = be2cpu(ReadU32(Ptr));
  276.     CapsInfo->encoder = TEncoderType(be2cpu(ReadU32(Ptr)));
  277.     CapsInfo->encrev = be2cpu(ReadU32(Ptr));
  278.     CapsInfo->release = be2cpu(ReadU32(Ptr));
  279.     CapsInfo->revision = be2cpu(ReadU32(Ptr));
  280.     CapsInfo->origin = be2cpu(ReadU32(Ptr));
  281.     CapsInfo->mincylinder = be2cpu(ReadU32(Ptr));
  282.     CapsInfo->maxcylinder = be2cpu(ReadU32(Ptr));
  283.     CapsInfo->minhead = be2cpu(ReadU32(Ptr));
  284.     CapsInfo->maxhead = be2cpu(ReadU32(Ptr));
  285.     ReadCapsDateTime(&CapsInfo->crdt, Ptr);
  286.     for(auto &v : CapsInfo->platform)
  287.     {
  288.         v = TPlatformId(be2cpu(ReadU32(Ptr)));
  289.     }
  290.     CapsInfo->disknum = be2cpu(ReadU32(Ptr));
  291.     CapsInfo->userid = be2cpu(ReadU32(Ptr));
  292.  
  293.     for(auto &v : CapsInfo->reserved)
  294.     {
  295.         v = be2cpu(ReadU32(Ptr));
  296.     }
  297. }
  298.  
  299. static void ReadCapsImage(TCapsImage *CapsImage, u8 *&Ptr)
  300. {
  301.     CapsImage->cylinder = be2cpu(ReadU32(Ptr));
  302.     CapsImage->head = be2cpu(ReadU32(Ptr));
  303.     CapsImage->dentype = TDensityType(be2cpu(ReadU32(Ptr)));
  304.     CapsImage->sigtype = TSigType(be2cpu(ReadU32(Ptr)));
  305.     CapsImage->trksize = be2cpu(ReadU32(Ptr));
  306.     CapsImage->startpos = be2cpu(ReadU32(Ptr));
  307.     CapsImage->startbit = be2cpu(ReadU32(Ptr));
  308.     CapsImage->databits = be2cpu(ReadU32(Ptr));
  309.     CapsImage->gapbits = be2cpu(ReadU32(Ptr));
  310.     CapsImage->trkbits = be2cpu(ReadU32(Ptr));
  311.     CapsImage->blkcnt = be2cpu(ReadU32(Ptr));
  312.     CapsImage->process = be2cpu(ReadU32(Ptr));
  313.     CapsImage->flag = be2cpu(ReadU32(Ptr));
  314.     CapsImage->did = be2cpu(ReadU32(Ptr));
  315.     for(auto &v : CapsImage->reserved)
  316.     {
  317.         v = be2cpu(ReadU32(Ptr));
  318.     }
  319. }
  320.  
  321. static void ReadCapsData(TCapsData *CapsData, u8 *&Ptr)
  322. {
  323.     CapsData->size = be2cpu(ReadU32(Ptr));
  324.     CapsData->bsize = be2cpu(ReadU32(Ptr));
  325.     CapsData->dcrc = be2cpu(ReadU32(Ptr));
  326.     CapsData->did = be2cpu(ReadU32(Ptr));
  327. }
  328.  
  329. static void ReadCapsBlockType(TCapsBlockType *CapsBlockType, TEncoderType EncoderType, u8 *&Ptr)
  330. {
  331.     switch(EncoderType)
  332.     {
  333.     case CAPS_ENCODER:
  334.         CapsBlockType->caps.blocksize = be2cpu(ReadU32(Ptr));
  335.         CapsBlockType->caps.gapsize = be2cpu(ReadU32(Ptr));
  336.         break;
  337.     case SPS_ENCODER:
  338.         CapsBlockType->sps.gapoffset = be2cpu(ReadU32(Ptr));
  339.         CapsBlockType->sps.celltype = be2cpu(ReadU32(Ptr));
  340.         break;
  341.     default:
  342.         assert(!"unknown encoder type");
  343.     }
  344. }
  345.  
  346. static void ReadCapsBlock(TCapsBlock *CapsBlock, TEncoderType EncoderType, u8 *&Ptr)
  347. {
  348.     CapsBlock->blockbits = be2cpu(ReadU32(Ptr));
  349.     CapsBlock->gapbits = be2cpu(ReadU32(Ptr));
  350.     ReadCapsBlockType(&CapsBlock->bt, EncoderType, Ptr);
  351.     CapsBlock->enctype = TEncType(be2cpu(ReadU32(Ptr)));
  352.     CapsBlock->flag.Flags = be2cpu(ReadU32(Ptr));
  353.     CapsBlock->gapvalue = be2cpu(ReadU32(Ptr));
  354.     CapsBlock->dataoffset = be2cpu(ReadU32(Ptr));
  355. }
  356.  
  357. static void ReadGapHdr(TGapStreamElementHdr *GapStreamElementHdr, u8 *&Ptr)
  358. {
  359.     GapStreamElementHdr->Hdr = *Ptr++;
  360. }
  361.  
  362. static u32 ReadVarUint(unsigned n, u8 *&Ptr)
  363. {
  364.     u32 v = 0;
  365.     for(unsigned i = 0; i < n; i++)
  366.     {
  367.         v |= unsigned(*Ptr) << (n - i - 1) * 8;
  368.         Ptr++;
  369.     }
  370.  
  371.     return v;
  372. }
  373.  
  374. static void ReadDataHdr(TDataStreamElementHdr *DataStreamElementHdr, u8 *&Ptr)
  375. {
  376.     DataStreamElementHdr->Hdr = *Ptr++;
  377. }
  378.  
  379. int FDD::read_ipf()
  380. {
  381.     u8 *Gptr = snbuf;
  382.  
  383.     u8 *Ptr = Gptr;
  384.     TCapsId CapsId;
  385.     ReadCapsId(&CapsId, Ptr);
  386.  
  387.     Gptr += CapsId.Size;
  388.     Ptr = Gptr;
  389.  
  390.     if(memcmp(CapsId.Name, ID_CAPS, sizeof(CapsId.Name)) != 0)
  391.     {
  392.         return 0;
  393.     }
  394.  
  395.     ReadCapsId(&CapsId, Ptr);
  396.     if(memcmp(CapsId.Name, ID_INFO, sizeof(CapsId.Name)) != 0)
  397.     {
  398.         return 0;
  399.     }
  400.  
  401.     TCapsInfo CapsInfo;
  402.     ReadCapsInfo(&CapsInfo, Ptr);
  403.  
  404.     Gptr += CapsId.Size;
  405.     Ptr = Gptr;
  406.     u8 *ImgeStart = Gptr; // ═рўрыю ьрёёштр чряшёхщ IMGE
  407.  
  408.     if(CapsInfo.maxcylinder >= MAX_CYLS)
  409.     {
  410.         err_printf("cylinders (%u) > MAX_CYLS(%d)", cyls, MAX_CYLS);
  411.         return 0;
  412.     }
  413.  
  414.     if(CapsInfo.maxhead >= 2)
  415.     {
  416.         err_printf("sides (%u) > 2", sides);
  417.         return 0;
  418.     }
  419.  
  420.     cyls = CapsInfo.maxcylinder + 1;
  421.     sides = CapsInfo.maxhead + 1;
  422.  
  423.     size_t mem = 0;
  424.     const unsigned bitmap_len = (unsigned(MAX_TRACK_LEN + 7U)) >> 3;
  425.  
  426.     mem += (MAX_CYLS - cyls) * sides * (MAX_TRACK_LEN + bitmap_len); // ─юсртър фы  тючьюцэюёЄш ЇюЁьрЄшЁютрэш  эр MAX_CYLS фюЁюцхъ
  427.  
  428.     rawsize = align_by(mem, 4096U);
  429.     rawdata = (unsigned char*)VirtualAlloc(nullptr, rawsize, MEM_COMMIT, PAGE_READWRITE);
  430.     u8 *dst = rawdata;
  431.  
  432.     auto Ncyls{ CapsInfo.maxcylinder - CapsInfo.mincylinder + 1 };
  433.     auto Nheads{ CapsInfo.maxhead - CapsInfo.minhead + 1 };
  434.  
  435.     u8 *DataStart2 = Gptr + (Ncyls * Nheads)*(sizeof(TCapsId) + sizeof(TCapsImage));
  436.  
  437.     // ═хшёяюы№чєхь√х ЄЁ¤ъш т эрўрых фшёър
  438.     for(unsigned c = 0; c < CapsInfo.mincylinder; c++)
  439.     {
  440.         for(unsigned h = 0; h < sides; h++)
  441.         {
  442.             trklen[c][h] = MAX_TRACK_LEN;
  443.             trkd[c][h] = dst;
  444.             dst += MAX_TRACK_LEN;
  445.  
  446.             trki[c][h] = dst;
  447.             dst += bitmap_len;
  448.         }
  449.     }
  450.  
  451.     for(unsigned c = CapsInfo.mincylinder; c <= CapsInfo.maxcylinder; c++)
  452.     {
  453.         for(unsigned h = CapsInfo.minhead; h <= CapsInfo.maxhead; h++)
  454.         {
  455.             ReadCapsId(&CapsId, Ptr);
  456.             assert(memcmp(CapsId.Name, ID_IMGE, sizeof(CapsId.Name)) == 0);
  457.  
  458.             TCapsImage CapsImage;
  459.             ReadCapsImage(&CapsImage, Ptr);
  460.             assert(CapsImage.cylinder == c && CapsImage.head == h);
  461.  
  462.             Gptr += CapsId.Size;
  463.             Ptr = Gptr;
  464.         }
  465.     }
  466.  
  467.     u8 *DataStart = Gptr; // ═рўрыю ьрёёштр чряшёхщ DATA
  468.     assert(DataStart == DataStart2);
  469.  
  470.     for(unsigned c = CapsInfo.mincylinder; c <= CapsInfo.maxcylinder; c++)
  471.     {
  472.         for(unsigned h = CapsInfo.minhead; h <= CapsInfo.maxhead; h++)
  473.         {
  474.             ReadCapsId(&CapsId, Ptr);
  475.             assert(memcmp(CapsId.Name, ID_DATA, sizeof(CapsId.Name)) == 0);
  476.  
  477.             TCapsData CapsData; // ┴ыюъш яхЁхьхээющ фышэ√
  478.             ReadCapsData(&CapsData, Ptr);
  479.  
  480.             u8 *PtrCapsBlock = Ptr;
  481.             Gptr += CapsId.Size + CapsData.size;
  482.  
  483.             Ptr = ImgeStart + (sizeof(TCapsId) + sizeof(TCapsImage)) * (CapsData.did - 1);
  484.  
  485.             ReadCapsId(&CapsId, Ptr);
  486.             assert(memcmp(CapsId.Name, ID_IMGE, sizeof(CapsId.Name)) == 0);
  487.  
  488.             TCapsImage CapsImage;
  489.             ReadCapsImage(&CapsImage, Ptr);
  490.             assert(CapsImage.cylinder == c && CapsImage.head == h);
  491.             assert(CapsImage.did == CapsData.did);
  492.  
  493. //            assert(CapsImage.trksize % 2 == 0);
  494.             unsigned sz = min(unsigned(CapsImage.trksize / 2), unsigned(MAX_TRACK_LEN)); // ╨рчьхЁ т сшЄют√ї  ўхщърї / 8
  495.             trklen[c][h] = sz;
  496.             trkd[c][h] = dst;
  497.  
  498.             u8 *dsti = dst + sz;
  499.             trki[c][h] = dsti;
  500.  
  501.             Ptr = PtrCapsBlock;
  502.  
  503.             u32 FixedGapSize = 0;
  504.  
  505.             // ╨рёўхЄ ёєььрЁэюую ЁрчьхЁр тёхї ЇшъёшЁютрээ√ї GAP'ют
  506.             for(unsigned i = 0; i < CapsImage.blkcnt; i++)
  507.             {
  508.                 TCapsBlock CapsBlock;
  509.                 ReadCapsBlock(&CapsBlock, CapsInfo.encoder, Ptr);
  510.                 u8 *Optr = Ptr;
  511.  
  512.                 Ptr = PtrCapsBlock + CapsBlock.bt.sps.gapoffset;
  513.  
  514.                 auto GalcGapSize = [&Ptr, &FixedGapSize]()
  515.                 {
  516.                     bool IsEnd = false;
  517.                     u32 Count = 0;
  518.                     do
  519.                     {
  520.                         TGapStreamElementHdr GapHdr;
  521.                         ReadGapHdr(&GapHdr, Ptr);
  522.  
  523.                         IsEnd = (GapHdr.GapSizeWidth == 0 && GapHdr.GapType == cpgapEnd);
  524.                         if(GapHdr.GapSizeWidth != 0)
  525.                         {
  526.                             u32 Size = ReadVarUint(GapHdr.GapSizeWidth, Ptr); // ┬хышўшэр т сшЄрї
  527.                             Size = (Size + 7) / 8; // ┬хышўшэр т срщЄрї
  528.                             switch(GapHdr.GapType)
  529.                             {
  530.                             case cpgapCount:
  531.                                 Count = Size;
  532.                                 break;
  533.                             case cpgapData:
  534.                             {
  535.                                 FixedGapSize += Count * Size;
  536.  
  537.                                 Ptr += Size;
  538.                                 break;
  539.                             }
  540.                             }
  541.                         }
  542.                     } while(!IsEnd);
  543.                 };
  544.  
  545.                 if(CapsBlock.flag.CAPS_BF_GP0) // Fw gap
  546.                 {
  547.                     GalcGapSize();
  548.                 }
  549.  
  550.                 if(CapsBlock.flag.CAPS_BF_GP1) // Bw gap
  551.                 {
  552.                     GalcGapSize();
  553.                 }
  554.  
  555.                 Ptr = Optr;
  556.             }
  557.  
  558.  
  559.             // ╧ючшЎш  т сшЄют√ї  ўхщърї / 8, ъюЁЁхъЎш  ъ ёЄрэфрЁЄэющ фышэх ЄЁ¤ър
  560.             // (ьэюушх ipf ╘рщы√ шьх■Є фышэє ЄЁ¤ър > 100000 сшЄют√ї  ўххъ)
  561.             // тшфшью юсЁрч√ ёэшьрыш эр фшёъютюфх ё чрэшцхээющ ёъюЁюёЄ№■ тЁр∙хэш  °яшэфхы 
  562.             // (фшёъютюф√ юЄ +3 эх шьх■Є ътрЁЎхтющ ёЄрсшышчрЎшш ёъюЁюёЄш тЁр∙хэш , ш шёяюы№чє■Є фтшурЄхы№ ё ярёёшъюь,
  563.             // р эх ёшэїЁюээ√щ ьэюуюяюы■ёэ√щ фтшурЄхы№ ё яЁ ь√ь яЁштюфюь)
  564.             // http://www.cpcwiki.eu/index.php/Amstrad_FDD_part (эр +3 яЁшьхэ ышё№ рэрыюушўэ√х фшёъютюф√)
  565.             u32 RealGapBits = MAX_TRACK_LEN * 16 - CapsImage.databits;
  566.             assert(RealGapBits >= FixedGapSize * 16);
  567.             u32 AvailGapBits = RealGapBits - FixedGapSize * 16;
  568.             u32 FlexGapBits = CapsImage.gapbits - FixedGapSize * 16;
  569.             u32 StartPos = (FlexGapBits != 0) ? (CapsImage.startbit * AvailGapBits) / (16 * FlexGapBits) : 0;
  570.             if(StartPos < 13)
  571.             {
  572.                 StartPos = 13; // 4E + 12(00)
  573.             }
  574.  
  575.             dst += StartPos;
  576.  
  577.             Ptr = PtrCapsBlock;
  578.  
  579.             for(unsigned i = 0; i < CapsImage.blkcnt; i++)
  580.             {
  581.                 TCapsBlock CapsBlock;
  582.                 ReadCapsBlock(&CapsBlock, CapsInfo.encoder, Ptr);
  583.                 u8 *Optr = Ptr;
  584.  
  585.                 unsigned GapBitsRemaining = CapsBlock.gapbits / 2; // ▌Єю эх сшЄ√, р MFM  ўхщъш (шї т 2 Ёрчр сюы№°х ўхь сшЄют)
  586.  
  587.                 assert(CapsInfo.encoder == SPS_ENCODER);
  588.  
  589.  
  590.                 Ptr = PtrCapsBlock + CapsBlock.dataoffset;
  591.  
  592.                 bool IsEnd = false;
  593.                 do
  594.                 {
  595.                     TDataStreamElementHdr DataHdr;
  596.                     ReadDataHdr(&DataHdr, Ptr);
  597.  
  598.                     IsEnd = (DataHdr.DataSizeWidth == 0 && DataHdr.DataType == cpdatEnd);
  599.                     if(DataHdr.DataSizeWidth != 0)
  600.                     {
  601.                         u32 Size = ReadVarUint(DataHdr.DataSizeWidth, Ptr);
  602.                         if(CapsBlock.flag.CAPS_BF_DMB) // ЁрчьхЁ фрээ√ї т сшЄрї
  603.                         {
  604. //                            assert(Size % 8 == 0);
  605.                             Size = (Size + 7) / 8;
  606.                         }
  607.                         switch(DataHdr.DataType)
  608.                         {
  609.                         case cpdatMark: // ╤шэїЁюьхЄъш чръюфшЁютрээ√х яю яЁртшырь MFM
  610.                         {
  611.                             u8 *Optr2 = Ptr;
  612.                             assert(Size % 2 == 0);
  613.                             for(unsigned j = 0; j < Size / 2; j++)
  614.                             {
  615.                                 u16 Mark = be2cpu(ReadU16(Ptr));
  616.                                 assert(dst + 1 - trkd[c][h] <= trklen[c][h]);
  617.                                 unsigned pos = unsigned(dst - trkd[c][h]);
  618.                                 switch(Mark)
  619.                                 {
  620.                                 case MFM_MARK_A1: // A1
  621.                                     *dst++ = 0xA1;
  622.                                     set_i(c, h, pos);
  623.                                     break;
  624.                                 case MFM_MARK_C2: // C2
  625.                                     *dst++ = 0xC2;
  626.                                     set_i(c, h, pos);
  627.                                     break;
  628.                                 default:
  629.                                     assert(!"unknown mark");
  630.                                 }
  631.                             }
  632.                             Ptr = Optr2;
  633.                             break;
  634.                         }
  635.                         case cpdatData: // ─рээ√х (т тшфх фхъюфшЁютрэ√ї срщЄют)
  636.                         case cpdatGap: // ╟ряюыэшЄхыш gap (т тшфх фхъюфшЁютрэ√ї срщЄют)
  637.                             assert(dst + Size - trkd[c][h] <= trklen[c][h]);
  638.                             memcpy(dst, Ptr, Size);
  639.                             dst += Size;
  640.                             break;
  641.                         default:
  642.                             assert(!"invalid data type");
  643.                         }
  644.                         Ptr += Size;
  645.                     }
  646.                 } while(!IsEnd);
  647.  
  648.                 Ptr = PtrCapsBlock + CapsBlock.bt.sps.gapoffset;
  649.  
  650.                 auto ProcessGap = [&Ptr, &dst, &GapBitsRemaining, StartPos, c, h, i, this]()
  651.                 {
  652.                     bool IsEnd = false;
  653.                     u32 Count = 0;
  654.                     u8 *GapPtr = nullptr;
  655.                     u32 GapDataSize = 0;
  656.                     do
  657.                     {
  658.                         TGapStreamElementHdr GapHdr;
  659.                         ReadGapHdr(&GapHdr, Ptr);
  660.  
  661.                         IsEnd = (GapHdr.GapSizeWidth == 0 && GapHdr.GapType == cpgapEnd);
  662.                         if(GapHdr.GapSizeWidth != 0)
  663.                         {
  664.                             u32 Size = ReadVarUint(GapHdr.GapSizeWidth, Ptr); // ┬хышўшэр т сшЄрї
  665.                             Size = (Size + 7) / 8; // ┬хышўшэр т срщЄрї
  666.                             switch(GapHdr.GapType)
  667.                             {
  668.                             case cpgapCount:
  669.                                 Count = Size;
  670.                                 break;
  671.                             case cpgapData:
  672.                             {
  673. //                                printf("c=%u,h=%u,i=%u,o=0x%X\n", c, h, i, unsigned(dst - trkd[c][h] + 0x13));
  674.  
  675.                                 if(Count == 0) // GAP яхЁхьхээющ фышэ√
  676.                                 {
  677.                                     if(dst - trkd[c][h] >= trklen[c][h]) // ═рўрыю ЄЁ¤ър
  678.                                     {
  679.                                         GapPtr = Ptr;
  680.                                         GapDataSize = Size;
  681.                                     }
  682.                                     else // ╩юэхЎ ЄЁ¤ър
  683.                                     {
  684.                                         Count = min(GapBitsRemaining / 8, unsigned(trklen[c][h] - (dst - trkd[c][h])));
  685.                                     }
  686.                                 }
  687.  
  688.                                 if(dst - trkd[c][h] < trklen[c][h])
  689.                                 {
  690.                                     while(Count)
  691.                                     {
  692.                                         assert(dst - trkd[c][h] < trklen[c][h]);
  693.                                         memcpy(dst, Ptr, Size);
  694.                                         dst += Size;
  695.                                         GapBitsRemaining -= Size * 8;
  696.                                         Count--;
  697.                                     }
  698.                                 }
  699.                                 else if(Count != 0)
  700.                                 {
  701.                                     assert(StartPos >= Count * Size);
  702.                                     dst = trkd[c][h] + StartPos - Count * Size;
  703.  
  704.                                     u32 CountOld = Count;
  705.                                     while(Count) // GAP ЇшъёшЁютрээющ фышэ√ яхЁхф C2C2C2FC
  706.                                     {
  707.                                         assert(dst - trkd[c][h] < trklen[c][h]);
  708.                                         memcpy(dst, Ptr, Size);
  709.                                         dst += Size;
  710.                                         GapBitsRemaining -= Size * 8;
  711.                                         Count--;
  712.                                     }
  713.  
  714.                                     if(GapPtr) // GAP яхЁхьхээющ фышэ√ т эрўрых ЄЁ¤ър
  715.                                     {
  716.                                         dst = trkd[c][h];
  717.                                         Count = StartPos - CountOld * Size;
  718.  
  719.                                         while(Count)
  720.                                         {
  721.                                             assert(dst - trkd[c][h] < trklen[c][h]);
  722.                                             memcpy(dst, GapPtr, GapDataSize);
  723.                                             dst += GapDataSize;
  724.                                             GapBitsRemaining -= GapDataSize * 8;
  725.                                             Count--;
  726.                                         }
  727.  
  728.                                         GapPtr = nullptr;
  729.                                     }
  730.                                 }
  731.                                 Ptr += Size;
  732.                                 break;
  733.                             }
  734.                             }
  735.                         }
  736.                     } while(!IsEnd);
  737.                 };
  738.  
  739.                 if(CapsBlock.flag.CAPS_BF_GP0) // Fw gap
  740.                 {
  741.                     ProcessGap();
  742.                 }
  743.  
  744.                 if(CapsBlock.flag.CAPS_BF_GP1) // Bw gap
  745.                 {
  746.                     ProcessGap();
  747.                 }
  748.  
  749. //                assert(GapBitsRemaining == 0);
  750.                 Ptr = Optr;
  751.             }
  752.  
  753.             dst += sz - (dst - trkd[c][h]);
  754.             assert(dst - trkd[c][h] == sz);
  755.             dst += (unsigned(sz + 7)) >> 3;
  756.             Ptr = Gptr;
  757.         }
  758.     }
  759.  
  760.     // ═хшёяюы№чєхь√х ЄЁ¤ъш т ъюэЎх фшёър
  761.     for(unsigned c = CapsInfo.maxcylinder + 1; c < MAX_CYLS; c++)
  762.     {
  763.         for(unsigned h = 0; h < sides; h++)
  764.         {
  765.             trklen[c][h] = MAX_TRACK_LEN;
  766.             trkd[c][h] = dst;
  767.             dst += MAX_TRACK_LEN;
  768.  
  769.             trki[c][h] = dst;
  770.             dst += bitmap_len;
  771.         }
  772.     }
  773.  
  774.     return 1;
  775. }
  776.