OXYGENHUB
◆ main openmsx / Contrib / dmk / svi2dmk.cc
4.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "dmk-common.hh"
2
3
#include <array>
4
#include <cassert>
5
#include <cstdint>
6
#include <cstdio>
7
#include <cstdlib>
8
#include <cstring>
9
#include <span>
10
#include <stdexcept>
11
#include <string>
12
#include <sys/stat.h>
13
#include <sys/types.h>
14
15
static const int TRACK_LENGTH = 6250;
16
static const int DMK_TRACK_LEN = TRACK_LENGTH + 128;
17
static const int NUM_CYLINDERS = 40;
18
static const int GAP1  = 50;
19
static const int GAP2  = 22;
20
static const int GAP3  = 34;
21
static const int GAP4a = 80;
22
23
24
static uint16_t calculateCrc(std::span<const uint8_t> buf)
25
{
26
	uint16_t crc = 0xffff;
27
	for (auto e : buf) {
28
		updateCrc(crc, e);
29
	}
30
	return crc;
31
}
32
33
static void fill(uint8_t*& p, int len, uint8_t value)
34
{
35
	memset(p, value, len);
36
	p += len;
37
}
38
39
static void convertTrack(int cyl, int head,
40
                         int sectorsPerTrack, int sectorSizeCode, int gap3,
41
                         const File& inf, const File& outf)
42
{
43
	int sectorSize = 128 << sectorSizeCode;
44
	int rawSectorLen = 12 + 10 + GAP2 + 12 + 4 + sectorSize + 2 + gap3;
45
	int gap4b = TRACK_LENGTH - (GAP4a + 12 + 4 + GAP1 + sectorsPerTrack * rawSectorLen);
46
	assert(gap4b > 0);
47
48
	std::array<uint8_t, DMK_TRACK_LEN> buf = {};
49
	uint8_t* ip = &buf[  0]; // pointer in IDAM table
50
	uint8_t* tp = &buf[128]; // pointer in actual track data
51
52
	fill(tp, GAP4a, 0x4e); // gap4a
53
	fill(tp,    12, 0x00); // sync
54
	fill(tp,     3, 0xc2); // index mark
55
	fill(tp,     1, 0xfc); //
56
	fill(tp,  GAP1, 0x4e); // gap1
57
	for (int sec = 0; sec < sectorsPerTrack; ++sec) {
58
		fill(tp,         12, 0x00); // sync
59
		fill(tp,          3, 0xa1); // ID addr mark
60
		int pos = tp - &buf[0];
61
		assert(pos < 0x4000);
62
		*ip++ = pos & 0xff;
63
		*ip++ = (pos >> 8) | 0x80; // double density (MFM) sector
64
		fill(tp,          1, 0xfe); // ID addr mark (cont)
65
		*tp++ = cyl;                // C
66
		*tp++ = head;               // H
67
		*tp++ = sec + 1;            // R
68
		*tp++ = sectorSizeCode;     // N
69
		auto addrCrc = calculateCrc(std::span{tp - 8, 8});
70
		*tp++ = addrCrc >> 8;       // CRC-1
71
		*tp++ = addrCrc & 0xff;     // CRC-2
72
		fill(tp, GAP2, 0x4e);       // gap2
73
		fill(tp,   12, 0x00);       // sync
74
		fill(tp,    3, 0xa1);       // data mark
75
		fill(tp,    1, 0xfb);       //
76
		inf.read(tp, sectorSize);
77
		auto dataCrc = calculateCrc(std::span{tp - 4, unsigned(sectorSize + 4)});
78
		tp += sectorSize;
79
		*tp++ = dataCrc >> 8;
80
		*tp++ = dataCrc & 0xff;
81
		fill(tp, gap3,  0x4e);      // gap3
82
	}
83
	fill(tp, gap4b, 0x4e); // gap4b
84
	assert((tp - &buf[0]) == DMK_TRACK_LEN);
85
86
	outf.write(&buf[0], DMK_TRACK_LEN);
87
}
88
89
static void convert(const char* input, const char* output)
90
{
91
	// Single or double sided input image?
92
	struct stat st;
93
	stat(input, &st);
94
	bool doubleSided;
95
	switch (st.st_size) {
96
	case 172032:
97
		doubleSided = false;
98
		break;
99
	case 346112:
100
		doubleSided = true;
101
		break;
102
	default:
103
		throw std::runtime_error(
104
			"input filesize should be exactly 172032 or 346112 bytes.\n");
105
	}
106
107
	// Write DMK header
108
	DmkHeader header;
109
	memset(&header, 0, sizeof(header));
110
	header.numTracks = NUM_CYLINDERS;
111
	header.trackLen[0] = DMK_TRACK_LEN & 0xff;
112
	header.trackLen[1] = DMK_TRACK_LEN >> 8;
113
	header.flags = (doubleSided ? (0 << 4) : (1 << 4)) |
114
	               (0 << 6); // double density (MFM)
115
	File outf(output, "wb");
116
	outf.write(&header, sizeof(header));
117
118
	// Read input image and convert to DMK, track per track
119
	File inf(input, "rb");
120
	int numSides = doubleSided ? 2 : 1;
121
	for (int cyl = 0; cyl < NUM_CYLINDERS; ++cyl) {
122
		for (int head = 0; head < numSides; ++head) {
123
			// first track has different layout
124
			int sectorsPerTrack;
125
			int sectorSizeCode;
126
			int gap3;
127
			if ((head == 0) && (cyl == 0)) {
128
				sectorsPerTrack = 18;
129
				sectorSizeCode = 0; // 128 bytes
130
				gap3 = 84;
131
			} else {
132
				sectorsPerTrack = 17;
133
				sectorSizeCode = 1; // 256 bytes
134
				gap3 = 34;
135
			}
136
			convertTrack(cyl, head,
137
			             sectorsPerTrack, sectorSizeCode, gap3,
138
			             inf, outf);
139
		}
140
	}
141
}
142
143
int main(int argc, const char** argv)
144
{
145
	std::span arg(argv, argc);
146
	if (arg.size() != 3) {
147
		printf("svi2dmk\n"
148
		       "-------\n"
149
		       "\n"
150
		       "Utility to convert a SVI disk image into a DMK disk image.\n"
151
		       "\n"
152
		       "The SVI image is expected to have to following format:\n"
153
		       " - 1st track (track 0, side 0) contains 18 sectors of 128 bytes.\n"
154
		       " - All other tracks contain 17 sectors of 256 bytes.\n"
155
		       " - There are 40 tracks on the disk.\n"
156
		       " - The disk can be either single or double sided.\n"
157
		       "This means that the size of the input image should be exactly\n"
158
		       "172032 bytes for single sided disks or 346112 bytes for double\n"
159
		       "sided disks.\n"
160
		       "\n"
161
		       "Usage: %s <input.dsk> <output.dmk>\n", arg[0]);
162
		exit(1);
163
	}
164
165
	try {
166
		convert(arg[1], arg[2]);
167
	} catch (std::exception& e) {
168
		fprintf(stderr, "Error: %s\n", e.what());
169
		exit(2);
170
	}
171
}