combine-dmk.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. #include <vector>
  2. #include <map>
  3. #include <algorithm>
  4. #include <string>
  5. #include <cstdio>
  6. #include <cassert>
  7. #include <cstring>
  8. #include <cstdlib>
  9. #include <sys/stat.h>
  10. using namespace std;
  11. typedef unsigned char byte;
  12. typedef unsigned short word;
  13. struct DmkHeader
  14. {
  15. byte writeProtected;
  16. byte numTracks;
  17. byte trackLen[2];
  18. byte flags;
  19. byte reserved[7];
  20. byte format[4];
  21. };
  22. ///////
  23. class Gaps
  24. {
  25. public:
  26. Gaps(int totalSize);
  27. void addInterval(int start, int stop);
  28. int getLargestGap();
  29. private:
  30. void addUse2(int start, int stop);
  31. const int totalSize;
  32. vector<int> v;
  33. };
  34. Gaps::Gaps(int totalSize_)
  35. : totalSize(totalSize_)
  36. {
  37. }
  38. // [start, stop)
  39. void Gaps::addInterval(int start, int stop)
  40. {
  41. start %= totalSize;
  42. stop %= totalSize;
  43. if (start < stop) {
  44. addUse2(start, stop);
  45. } else if (start > stop) {
  46. addUse2(start, totalSize);
  47. if (stop != 0) addUse2(0, stop);
  48. }
  49. }
  50. void Gaps::addUse2(int start, int stop)
  51. {
  52. assert(start < stop);
  53. assert(stop <= totalSize);
  54. v.push_back(2 * start + 0);
  55. v.push_back(2 * stop + 1);
  56. }
  57. int Gaps::getLargestGap()
  58. {
  59. if (v.empty()) return totalSize / 2;
  60. // sort begin and end point
  61. sort(v.begin(), v.end());
  62. // largest gap found so far (and its start and stop position)
  63. int maxLen = 0;
  64. int maxStart = 0;
  65. int maxStop = 0;
  66. // last end point is start of first gap
  67. int start = v.back() / 2;
  68. if (start == totalSize) start = 0;
  69. int count = 0;
  70. for (vector<int>::const_iterator it = v.begin();
  71. it != v.end(); ++it) {
  72. int i = *it;
  73. if (i & 1) {
  74. // interval end point
  75. --count;
  76. // no more overlapping intervals -> start of new gap
  77. if (count == 0) start = i / 2;
  78. } else {
  79. // interval begin point
  80. int stop = i / 2;
  81. if (count == 0) {
  82. // found gap: [start, stop)
  83. int len = stop - start;
  84. if (len < 0) len += totalSize;
  85. if (len > maxLen) {
  86. maxLen = len;
  87. maxStart = start;
  88. maxStop = stop;
  89. }
  90. }
  91. ++count;
  92. }
  93. }
  94. assert(count == 0);
  95. if (maxLen == 0) return -1;
  96. if (maxStop < maxStart) maxStop += totalSize;
  97. int mid = (maxStart + maxStop) / 2;
  98. return mid % totalSize;
  99. }
  100. ///////
  101. static byte readCircular(const vector<byte>& buffer, int idx)
  102. {
  103. int dmkTrackLen = buffer.size();
  104. return buffer[128 + idx % (dmkTrackLen - 128)];
  105. }
  106. static void updateCrc(word& crc, byte val)
  107. {
  108. for (int i = 8; i < 16; ++i) {
  109. crc = (crc << 1) ^ ((((crc ^ (val << i)) & 0x8000) ? 0x1021 : 0));
  110. }
  111. }
  112. static void verifyDMK(bool b, const char* message)
  113. {
  114. if (!b) {
  115. fprintf(stderr, "Invalid input: %s\n", message);
  116. exit(1);
  117. }
  118. }
  119. static int analyzeTrack(vector<byte>& buffer)
  120. {
  121. int dmkTrackLen = buffer.size();
  122. int trackLen = dmkTrackLen - 128;
  123. Gaps gaps(trackLen);
  124. for (int i = 0; i < 64; ++i) {
  125. // Get (and check) pointer into track data
  126. int dmkIdx = buffer[2 * i + 0] + 256 * buffer[2 * i + 1];
  127. if (dmkIdx == 0) {
  128. // end of table reached
  129. break;
  130. }
  131. verifyDMK((dmkIdx & 0xC000) == 0x8000, "double density flag");
  132. dmkIdx &= ~0xC000; // clear flags
  133. verifyDMK(dmkIdx >= 128, "IDAM offset too small");
  134. verifyDMK(dmkIdx < dmkTrackLen, "IDAM offset too large");
  135. dmkIdx -= 128;
  136. // read address mark
  137. int addrIdx = dmkIdx - 3; // might be negative
  138. byte d0 = readCircular(buffer, addrIdx + 0);
  139. byte d1 = readCircular(buffer, addrIdx + 1);
  140. byte d2 = readCircular(buffer, addrIdx + 2);
  141. byte d3 = readCircular(buffer, addrIdx + 3);
  142. byte c = readCircular(buffer, addrIdx + 4);
  143. byte h = readCircular(buffer, addrIdx + 5);
  144. byte r = readCircular(buffer, addrIdx + 6);
  145. byte n = readCircular(buffer, addrIdx + 7);
  146. byte ch = readCircular(buffer, addrIdx + 8);
  147. byte cl = readCircular(buffer, addrIdx + 9);
  148. // address mark CRC
  149. word addrCrc = 0xFFFF;
  150. updateCrc(addrCrc, d0);
  151. updateCrc(addrCrc, d1);
  152. updateCrc(addrCrc, d2);
  153. updateCrc(addrCrc, d3);
  154. updateCrc(addrCrc, c);
  155. updateCrc(addrCrc, h);
  156. updateCrc(addrCrc, r);
  157. updateCrc(addrCrc, n);
  158. word onDiskAddrCrc = 256 * ch + cl;
  159. if (onDiskAddrCrc != addrCrc) {
  160. // only mark address mark as in-use
  161. gaps.addInterval(addrIdx, addrIdx + 10);
  162. continue;
  163. }
  164. // locate data mark, should be within 43 bytes from end
  165. // of address mark (according to WD2793 datasheet)
  166. for (int i = 10; i < 53; ++i) {
  167. int dataIdx = addrIdx + i;
  168. byte a0 = readCircular(buffer, dataIdx + 0);
  169. byte a1 = readCircular(buffer, dataIdx + 1);
  170. byte a2 = readCircular(buffer, dataIdx + 2);
  171. byte t = readCircular(buffer, dataIdx + 3);
  172. if ((a0 != 0xA1) || (a1 != 0xA1) || (a2 != 0xA1) ||
  173. ((t != 0xFB) && (t != 0xF8))) {
  174. continue;
  175. }
  176. // Mark whole region from begin of address mark to end
  177. // of data as in-use (so including the gap between
  178. // address and data section).
  179. int sectorSize = 128 << (n & 3);
  180. int end = dataIdx + 4 + sectorSize + 2; // header + data + crc
  181. gaps.addInterval(addrIdx, end);
  182. break;
  183. }
  184. // if (i == 53) --> data mark not found
  185. }
  186. return gaps.getLargestGap();
  187. }
  188. int main()
  189. {
  190. vector<vector<byte> > data; // buffer all .DAT files
  191. string name = "DMK-tt-s.DAT";
  192. for (int t = 0; t <= 99; ++t) {
  193. for (int h = 0; h < 2; ++h) {
  194. name[4] = (t / 10) + '0';
  195. name[5] = (t % 10) + '0';
  196. name[7] = h + '0';
  197. FILE* file = fopen(name.c_str(), "rb");
  198. if (!file) {
  199. if (h == 0) goto done_read;
  200. fprintf(stderr,
  201. "Couldn't open file %s, but the "
  202. "corresponding file for side 0 was "
  203. "found.\n",
  204. name.c_str());
  205. exit(1);
  206. }
  207. struct stat st;
  208. fstat(fileno(file), &st);
  209. size_t size = st.st_size;
  210. if (size < 128) {
  211. fprintf(stderr, "File %s is too small.\n",
  212. name.c_str());
  213. exit(1);
  214. }
  215. vector<byte> dat(size);
  216. if (fread(dat.data(), size, 1, file) != 1) {
  217. fprintf(stderr, "Error reading file %s.\n",
  218. name.c_str());
  219. exit(1);
  220. }
  221. data.push_back(dat);
  222. }
  223. }
  224. done_read:
  225. assert((data.size() & 1) == 0);
  226. int numTracks = data.size() / 2;
  227. // Check that no .dat files with higher track number are found.
  228. for (int t = numTracks; t <= 99; ++t) {
  229. for (int h = 0; h < 2; ++h) {
  230. name[4] = (t / 10) + '0';
  231. name[5] = (t % 10) + '0';
  232. name[7] = h + '0';
  233. FILE* file = fopen(name.c_str(), "rb");
  234. if (!file) continue; // ok, we should have this file
  235. string name2 = "DMK-tt-0.DAT";
  236. name2[4] = (numTracks / 10) + '0';
  237. name2[5] = (numTracks % 10) + '0';
  238. fprintf(stderr,
  239. "Found file %s, but file %s is missing.\n",
  240. name.c_str(), name2.c_str());
  241. exit(1);
  242. }
  243. }
  244. printf("Found .dat files for %d tracks (double sided).\n", numTracks);
  245. // Create histogram of tracklengths.
  246. map<unsigned, unsigned> sizes; // length, count
  247. for (vector<vector<byte> >::iterator it = data.begin();
  248. it != data.end(); ++it) {
  249. unsigned size = it->size();
  250. ++sizes[size];
  251. }
  252. // Search the peak in this histogram (= the tracklength that occurs
  253. // most often).
  254. unsigned maxCount = 0;
  255. unsigned trackSize = 0;
  256. for (map<unsigned, unsigned>::const_iterator it = sizes.begin();
  257. it != sizes.end(); ++it) {
  258. if (it->second >= maxCount) {
  259. maxCount = it->second;
  260. trackSize = it->first;
  261. }
  262. }
  263. // Open output file.
  264. FILE* file = fopen("out.dmk", "wb+");
  265. if (!file) {
  266. fprintf(stderr, "Couldn't open output file out.dmk.\n");
  267. exit(1);
  268. }
  269. // Write DMK header.
  270. DmkHeader header;
  271. memset(&header, 0, sizeof(header));
  272. header.numTracks = numTracks;
  273. header.trackLen[0] = trackSize % 256;
  274. header.trackLen[1] = trackSize / 256;
  275. if (fwrite(&header, sizeof(header), 1, file) != 1) {
  276. fprintf(stderr, "Error writing out.dmk.\n");
  277. exit(1);
  278. }
  279. // Process each track.
  280. for (vector<vector<byte> >::iterator it = data.begin();
  281. it != data.end(); ++it) {
  282. vector<byte>& v = *it;
  283. // Adjust track size
  284. while (v.size() != trackSize) {
  285. // Locate (middle of) largest gap in this track.
  286. int mid = analyzeTrack(v) + 128;
  287. if (mid == -1) {
  288. fprintf(stderr, "Couldn't adjust track length.\n");
  289. exit(1);
  290. }
  291. assert(mid < int(v.size()));
  292. // We insert or delete one byte at a time. This may not
  293. // be the most efficient approach (but still more than
  294. // fast enough, we typically only nned to adjust a few
  295. // bytes anyway). This has the advantage of being very
  296. // simple: it can easily handle gaps that wrap around
  297. // from the end to the beginning of the track and it
  298. // can handle the case that after decreasing a gap with
  299. // a few bytes another gaps becomes the largest gap.
  300. int delta;
  301. if (trackSize > v.size()) {
  302. delta = 1;
  303. v.insert(v.begin() + mid, v[mid]);
  304. } else {
  305. delta = -1;
  306. v.erase(v.begin() + mid);
  307. }
  308. // After inserting/deleting byte(s), we need to adjust
  309. // the IDAM table.
  310. for (int i = 0; i < 64; ++i) {
  311. int t = v[2 * i + 0] + 256 * v[2 * i + 1];
  312. if (t == 0) break;
  313. if ((t & 0x3fff) > mid) t += delta;
  314. v[2 * i + 0] = t % 256;
  315. v[2 * i + 1] = t / 256;
  316. }
  317. }
  318. // Write track to DMK file.
  319. if (fwrite(v.data(), v.size(), 1, file) != 1) {
  320. fprintf(stderr, "Error writing out.dmk.\n");
  321. exit(1);
  322. }
  323. }
  324. if (fclose(file)) {
  325. fprintf(stderr, "Error closing out.dmk.\n");
  326. exit(1);
  327. }
  328. printf("Successfully wrote out.dmk.\n");
  329. exit(0);
  330. }