OXYGENHUB
◆ main openmsx / Contrib / dmk / der2dmk.cc
5.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "dmk-common.hh"
2
3
#include <array>
4
#include <cassert>
5
#include <cstdint>
6
#include <cstdlib>
7
#include <cstring>
8
#include <span>
9
#include <string>
10
#include <sys/stat.h>
11
#include <sys/types.h>
12
#include <vector>
13
14
15
struct DiskInfo
16
{
17
	int gap1;
18
	int gap2;
19
	int gap3;
20
	int gap4a;
21
	int gap4b;
22
	int sectorsPerTrack;
23
	int numberCylinders;
24
	int sectorSizeCode;
25
	bool doubleSided;
26
};
27
28
static void fill(uint8_t*& p, int len, uint8_t value)
29
{
30
	memset(p, value, len);
31
	p += len;
32
}
33
34
void convert(const DiskInfo& info, const std::string& dsk,
35
             const std::string& der, const std::string& dmk)
36
{
37
	int numSides = info.doubleSided ? 2 : 1;
38
	int sectorSize = 128 << info.sectorSizeCode;
39
	int totalTracks = numSides * info.numberCylinders;
40
	int totalSectors = totalTracks * info.sectorsPerTrack;
41
	int totalSize = totalSectors * sectorSize;
42
43
	struct stat st;
44
	stat(dsk.c_str(), &st);
45
	if (st.st_size != totalSize) {
46
		throw std::runtime_error("Wrong input filesize");
47
	}
48
49
	File inf (dsk, "rb");
50
	File derf(der, "rb");
51
	File outf(dmk, "wb");
52
53
	static const char* const DER_HEADER = "DiskImage errors\r\n\032";
54
	std::array<char, 20> derHeader;
55
	derf.read(derHeader.data(), 20);
56
	if (memcmp(derHeader.data(), DER_HEADER, 20) != 0) {
57
		throw std::runtime_error("Invalid .der file.");
58
	}
59
	int derSize = (totalSectors + 7)  / 8;
60
	std::vector<uint8_t> derBuf(derSize);
61
	derf.read(derBuf.data(), derSize);
62
63
	int rawSectorLen =
64
		12 + 10 + info.gap2 + 12 + 4 +
65
		sectorSize + 2 + info.gap3;
66
	int rawTrackLen =
67
		info.gap4a + 12 + 4 + info.gap1 +
68
		info.sectorsPerTrack * rawSectorLen + info.gap4b;
69
	assert(rawTrackLen == 6250);
70
	int dmkTrackLen = rawTrackLen + 128;
71
72
	DmkHeader header;
73
	memset(&header, 0, sizeof(header));
74
	header.numTracks = info.numberCylinders;
75
	header.trackLen[0] = dmkTrackLen & 0xff;
76
	header.trackLen[1] = dmkTrackLen >> 8;
77
	header.flags = (info.doubleSided ? (0 << 4) : (1 << 4)) |
78
	               (0 << 6); // double density (MFM)
79
	outf.write(&header, sizeof(header));
80
81
	std::vector<uint8_t*> addrPos(info.sectorsPerTrack);
82
	std::vector<uint8_t*> dataPos(info.sectorsPerTrack);
83
	std::vector<uint8_t> buf(dmkTrackLen); // zero-initialized
84
	uint8_t* ip = buf.data() +   0; // pointer in IDAM table
85
	uint8_t* tp = buf.data() + 128; // pointer in actual track data
86
87
	fill(tp, info.gap4a, 0x4e); // gap4a
88
	fill(tp,         12, 0x00); // sync
89
	fill(tp,          3, 0xc2); // index mark
90
	fill(tp,          1, 0xfc); //
91
	fill(tp, info.gap1,  0x4e); // gap1
92
	for (int sec = 0; sec < info.sectorsPerTrack; ++sec) {
93
		fill(tp,         12, 0x00); // sync
94
		fill(tp,          3, 0xa1); // ID addr mark
95
		int pos = tp - buf.data();
96
		assert(pos < 0x4000);
97
		*ip++ = pos & 0xff;
98
		*ip++ = (pos >> 8) | 0x80; // double density (MFM) sector
99
		fill(tp,          1, 0xfe); // ID addr mark (cont)
100
		addrPos[sec] = tp;
101
		fill(tp,          6, 0x00); // C H R N CRC (overwritten later)
102
		fill(tp, info.gap2,  0x4e); // gap2
103
		fill(tp,         12, 0x00); // sync
104
		fill(tp,          3, 0xa1); // data mark
105
		fill(tp,          1, 0xfb); //
106
		dataPos[sec] = tp;
107
		fill(tp, sectorSize, 0x00); // sector data (overwritten later)
108
		fill(tp,          2, 0x00); // CRC (overwritten later)
109
		fill(tp, info.gap3,  0x4e); // gap3
110
	}
111
	fill(tp, info.gap4b, 0x4e); // gap4b
112
	assert((tp - buf.data()) == dmkTrackLen);
113
114
	int derCount = 0;
115
	for (int cyl = 0; cyl < info.numberCylinders; ++cyl) {
116
		for (int head = 0; head < numSides; ++head) {
117
			for (int sec = 0; sec < info.sectorsPerTrack; ++sec) {
118
				uint8_t* ap = addrPos[sec];
119
				*ap++ = cyl;
120
				*ap++ = head;
121
				*ap++ = sec + 1;
122
				*ap++ = info.sectorSizeCode;
123
124
				uint16_t addrCrc = 0xffff;
125
				assert(ap >= &buf[8]);
126
				const uint8_t* t1 = ap - 8;
127
				for (int i = 0; i < 8; ++i) {
128
					updateCrc(addrCrc, t1[i]);
129
				}
130
				*ap++ = addrCrc >> 8;
131
				*ap++ = addrCrc & 0xff;
132
133
				uint8_t* dp = dataPos[sec];
134
				inf.read(dp, sectorSize);
135
				dp += sectorSize;
136
137
				uint16_t dataCrc = 0xffff;
138
				const uint8_t* t2 = dp - sectorSize - 4;
139
				for (int i = 0; i < sectorSize + 4; ++i) {
140
					updateCrc(dataCrc, t2[i]);
141
				}
142
				if (derBuf[derCount / 8] & (0x80 >> (derCount % 8))) {
143
					// create CRC error for this sector
144
					dataCrc ^= 0xffff;
145
				}
146
				++derCount;
147
				*dp++ = dataCrc >> 8;
148
				*dp++ = dataCrc & 0xff;
149
			}
150
			outf.write(buf.data(), dmkTrackLen);
151
		}
152
	}
153
}
154
155
int main(int argc, const char** argv)
156
{
157
	std::span arg(argv, argc);
158
	if (arg.size() != 4) {
159
		printf("der2dmk\n"
160
		       "\n"
161
		       "Utility to convert a dsk disk image with associated disk\n"
162
		       "error file into a dmk disk image. At the moment this utility\n"
163
		       "is limited to 720kB double sided, double density dsk images.\n"
164
		       "\n"
165
		       "Usage: %s <input.dsk> <input.der> <output.dmk>\n", arg[0]);
166
		exit(1);
167
	}
168
169
	// TODO add command line options to make these parameters configurable
170
	DiskInfo info;
171
	info.gap1  =  50;
172
	info.gap2  =  22;
173
	info.gap3  =  84;
174
	info.gap4a =  80;
175
	info.gap4b = 182; // TODO calculate from other values
176
	info.sectorsPerTrack = 9;
177
	info.numberCylinders = 80;
178
	info.sectorSizeCode = 2; // 512 = 128 << 2
179
	info.doubleSided = true;
180
181
	try {
182
		convert(info, arg[1], arg[2], arg[3]);
183
	} catch (std::exception& e) {
184
		fprintf(stderr, "Error: %s\n", e.what());
185
	}
186
}