mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Convert scptoflux to a proper flux source.
This commit is contained in:
		| @@ -33,6 +33,9 @@ std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config) | ||||
|  | ||||
| 		case FluxSourceProto::kTestPattern: | ||||
| 			return createTestPatternFluxSource(config.test_pattern()); | ||||
|  | ||||
| 		case FluxSourceProto::kScp: | ||||
| 			return createScpFluxSource(config.scp()); | ||||
| 	} | ||||
|  | ||||
| 	Error() << "bad input disk configuration"; | ||||
| @@ -45,6 +48,7 @@ void FluxSource::updateConfigForFilename(const std::string& filename) | ||||
| 	static const std::vector<std::pair<std::regex, std::function<void(const std::string&)>>> formats = | ||||
| 	{ | ||||
| 		{ std::regex("^(.*\\.flux)$"),     [&](const auto& s) { f->set_fluxfile(s); }}, | ||||
| 		{ std::regex("^(.*\\.scp)$"),      [&](const auto& s) { f->mutable_scp()->set_filename(s); }}, | ||||
| 		{ std::regex("^erase:$"),          [&](const auto& s) { f->mutable_erase(); }}, | ||||
| 		{ std::regex("^kryoflux:(.*)$"),   [&](const auto& s) { f->mutable_kryoflux()->set_directory(s); }}, | ||||
| 		{ std::regex("^testpattern:(.*)"), [&](const auto& s) { f->mutable_test_pattern(); }}, | ||||
|   | ||||
| @@ -10,6 +10,7 @@ class HardwareFluxSourceProto; | ||||
| class TestPatternFluxSourceProto; | ||||
| class EraseFluxSourceProto; | ||||
| class KryofluxFluxSourceProto; | ||||
| class ScpFluxSourceProto; | ||||
|  | ||||
| class FluxSource | ||||
| { | ||||
| @@ -20,6 +21,7 @@ private: | ||||
|     static std::unique_ptr<FluxSource> createSqliteFluxSource(const std::string& filename); | ||||
|     static std::unique_ptr<FluxSource> createHardwareFluxSource(const HardwareFluxSourceProto& config); | ||||
|     static std::unique_ptr<FluxSource> createKryofluxFluxSource(const KryofluxFluxSourceProto& config); | ||||
|     static std::unique_ptr<FluxSource> createScpFluxSource(const ScpFluxSourceProto& config); | ||||
|     static std::unique_ptr<FluxSource> createTestPatternFluxSource(const TestPatternFluxSourceProto& config); | ||||
|     static std::unique_ptr<FluxSource> createEraseFluxSource(const EraseFluxSourceProto& config); | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,11 @@ message KryofluxFluxSourceProto { | ||||
| 	optional string directory = 1 [(help) = "path to Kryoflux stream directory"]; | ||||
| } | ||||
|  | ||||
| message ScpFluxSourceProto { | ||||
| 	optional string filename = 1 [default = "flux.scp", | ||||
| 		(help) = ".scp file to read flux from"]; | ||||
| } | ||||
|  | ||||
| message FluxSourceProto { | ||||
| 	oneof source { | ||||
| 		string fluxfile = 1; | ||||
| @@ -31,6 +36,7 @@ message FluxSourceProto { | ||||
| 		TestPatternFluxSourceProto test_pattern = 3; | ||||
| 		EraseFluxSourceProto erase = 4; | ||||
| 		KryofluxFluxSourceProto kryoflux = 5; | ||||
| 		ScpFluxSourceProto scp = 6; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										130
									
								
								lib/fluxsource/scpfluxsource.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								lib/fluxsource/scpfluxsource.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| #include "globals.h" | ||||
| #include "fluxmap.h" | ||||
| #include "kryoflux.h" | ||||
| #include "lib/fluxsource/fluxsource.pb.h" | ||||
| #include "fluxsource/fluxsource.h" | ||||
| #include "scp.h" | ||||
| #include "proto.h" | ||||
| #include "fmt/format.h" | ||||
| #include <fstream> | ||||
|  | ||||
| static int trackno(int strack) | ||||
| { | ||||
|     return strack >> 1; | ||||
| } | ||||
|  | ||||
| static int headno(int strack) | ||||
| { | ||||
|     return strack & 1; | ||||
| } | ||||
|  | ||||
| static int strackno(int track, int side) | ||||
| { | ||||
| 	return (track << 1) | side; | ||||
| } | ||||
|  | ||||
| class ScpFluxSource : public FluxSource | ||||
| { | ||||
| public: | ||||
|     ScpFluxSource(const ScpFluxSourceProto& config): | ||||
|         _config(config) | ||||
|     { | ||||
| 		_if.open(_config.filename(), std::ios::in | std::ios::binary); | ||||
| 		if (!_if.is_open()) | ||||
| 			Error() << fmt::format("cannot open input file '{}': {}", _config.filename(), strerror(errno)); | ||||
|  | ||||
| 		_if.read((char*) &_header, sizeof(_header)); | ||||
| 		check_for_error(); | ||||
|  | ||||
| 		if ((_header.file_id[0] != 'S') | ||||
| 				|| (_header.file_id[1] != 'C') | ||||
| 				|| (_header.file_id[2] != 'P')) | ||||
| 			Error() << "input not a SCP file"; | ||||
|  | ||||
| 		_resolution = 25 * (_header.resolution + 1); | ||||
| 		int startSide = (_header.heads == 2) ? 1 : 0; | ||||
| 		int endSide = (_header.heads == 1) ? 0 : 1; | ||||
|  | ||||
| 		if ((_header.cell_width != 0) && (_header.cell_width != 16)) | ||||
| 			Error() << "currently only 16-bit cells in SCP files are supported"; | ||||
|  | ||||
| 		std::cout << fmt::format("SCP tracks {}-{}, heads {}-{}\n", | ||||
| 			trackno(_header.start_track), trackno(_header.end_track), startSide, endSide); | ||||
| 		std::cout << fmt::format("SCP sample resolution: {} ns\n", _resolution); | ||||
| 	} | ||||
|  | ||||
| public: | ||||
|     std::unique_ptr<Fluxmap> readFlux(int track, int side) | ||||
|     { | ||||
| 		int strack = strackno(track, side); | ||||
| 		uint32_t offset = Bytes(_header.track[strack], 4).reader().read_le32(); | ||||
| 		if (offset == 0) | ||||
| 			return std::unique_ptr<Fluxmap>(); | ||||
|  | ||||
| 		ScpTrack trackheader; | ||||
| 		_if.seekg(offset, std::ios::beg); | ||||
| 		_if.read((char*) &trackheader, sizeof(trackheader)); | ||||
| 		check_for_error(); | ||||
|  | ||||
| 		if ((trackheader.track_id[0] != 'T') | ||||
| 				|| (trackheader.track_id[1] != 'R') | ||||
| 				|| (trackheader.track_id[2] != 'K')) | ||||
| 			Error() << "corrupt SCP file"; | ||||
|  | ||||
| 		std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
| 		nanoseconds_t pending = 0; | ||||
| 		unsigned inputBytes = 0; | ||||
| 		for (int revolution = 0; revolution < _header.revolutions; revolution++) | ||||
| 		{ | ||||
| 			if (revolution != 0) | ||||
| 				fluxmap->appendIndex(); | ||||
|  | ||||
| 			uint32_t datalength = Bytes(trackheader.revolution[revolution].length, 4).reader().read_le32(); | ||||
| 			uint32_t dataoffset = Bytes(trackheader.revolution[revolution].offset, 4).reader().read_le32(); | ||||
|  | ||||
| 			Bytes data(datalength*2); | ||||
| 			_if.seekg(dataoffset + offset, std::ios::beg); | ||||
| 			_if.read((char*) data.begin(), data.size()); | ||||
| 			check_for_error(); | ||||
|  | ||||
| 			ByteReader br(data); | ||||
| 			for (int cell = 0; cell < datalength; cell++) | ||||
| 			{ | ||||
| 				uint16_t interval = br.read_be16(); | ||||
| 				if (interval) | ||||
| 				{ | ||||
| 					fluxmap->appendInterval((interval + pending) * _resolution / NS_PER_TICK); | ||||
| 					fluxmap->appendPulse(); | ||||
| 					pending = 0; | ||||
| 				} | ||||
| 				else | ||||
| 					pending += 0x10000; | ||||
| 			} | ||||
|  | ||||
| 			inputBytes += datalength*2; | ||||
| 		} | ||||
|  | ||||
| 		return fluxmap; | ||||
|     } | ||||
|  | ||||
|     void recalibrate() {} | ||||
|  | ||||
| private: | ||||
| 	void check_for_error() | ||||
| 	{ | ||||
| 		if (_if.fail()) | ||||
| 			Error() << fmt::format("SCP read I/O error: {}", strerror(errno)); | ||||
| 	} | ||||
|  | ||||
| private: | ||||
|     const ScpFluxSourceProto& _config; | ||||
| 	std::ifstream _if; | ||||
| 	ScpHeader _header; | ||||
| 	nanoseconds_t _resolution; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<FluxSource> FluxSource::createScpFluxSource(const ScpFluxSourceProto& config) | ||||
| { | ||||
|     return std::unique_ptr<FluxSource>(new ScpFluxSource(config)); | ||||
| } | ||||
|  | ||||
| @@ -27,6 +27,8 @@ void Track::readFluxmap() | ||||
| { | ||||
| 	std::cout << fmt::format("{0:>3}.{1}: ", physicalTrack, physicalSide) << std::flush; | ||||
| 	fluxmap = fluxsource->readFlux(physicalTrack, physicalSide); | ||||
| 	if (!fluxmap) | ||||
| 		fluxmap.reset(new Fluxmap()); | ||||
| 	std::cout << fmt::format( | ||||
| 		"{0} ms in {1} bytes\n", | ||||
|             fluxmap->duration()/1e6, | ||||
| @@ -35,42 +37,6 @@ void Track::readFluxmap() | ||||
| 		outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap); | ||||
| } | ||||
|  | ||||
| #if 0 | ||||
| std::vector<std::unique_ptr<Track>> readTracks(FluxSource& fluxSource) | ||||
| { | ||||
|     const FluxSpec spec(source); | ||||
|  | ||||
|     std::cout << "Reading from: " << source << std::endl; | ||||
|  | ||||
| 	if (!destination.get().empty()) | ||||
| 	{ | ||||
| 		std::cout << "Writing a copy of the flux to " << destination.get() << std::endl; | ||||
| 		outputFluxSink = FluxSink::createSqliteFluxSink(destination.get()); | ||||
| 	} | ||||
|  | ||||
| 	std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec); | ||||
|  | ||||
| 	std::vector<std::unique_ptr<Track>> tracks; | ||||
|     for (const auto& location : spec.locations) | ||||
| 	{ | ||||
| 		auto track = std::make_unique<Track>(location.track, location.side); | ||||
| 		track->fluxsource = fluxSource; | ||||
| 		tracks.push_back(std::move(track)); | ||||
| 	} | ||||
|  | ||||
| 	if (justRead) | ||||
| 	{ | ||||
| 		for (auto& track : tracks) | ||||
| 			track->readFluxmap(); | ||||
| 		 | ||||
| 		std::cout << "--just-read specified, halting now" << std::endl; | ||||
| 		exit(0); | ||||
| 	} | ||||
|  | ||||
| 	return tracks; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static bool conflictable(Sector::Status status) | ||||
| { | ||||
| 	return (status == Sector::OK) || (status == Sector::CONFLICT); | ||||
|   | ||||
| @@ -325,6 +325,7 @@ buildlibrary libbackend.a \ | ||||
|     lib/fluxsource/hardwarefluxsource.cc \ | ||||
|     lib/fluxsource/kryoflux.cc \ | ||||
|     lib/fluxsource/kryofluxfluxsource.cc \ | ||||
|     lib/fluxsource/scpfluxsource.cc \ | ||||
|     lib/fluxsource/sqlitefluxsource.cc \ | ||||
|     lib/fluxsource/testpatternfluxsource.cc \ | ||||
|     lib/globals.cc \ | ||||
|   | ||||
| @@ -26,7 +26,7 @@ static StringFlag sourceFlux( | ||||
| 	"", | ||||
| 	[](const auto& value) | ||||
| 	{ | ||||
| 		config.mutable_input()->mutable_flux()->set_fluxfile(value); | ||||
| 		FluxSource::updateConfigForFilename(value); | ||||
| 	}); | ||||
|  | ||||
| static IntFlag sourceDrive( | ||||
|   | ||||
| @@ -25,7 +25,7 @@ static StringFlag sourceFlux( | ||||
| 	"", | ||||
| 	[](const auto& value) | ||||
| 	{ | ||||
| 		config.mutable_input()->mutable_flux()->set_fluxfile(value); | ||||
| 		FluxSource::updateConfigForFilename(value); | ||||
| 	}); | ||||
|  | ||||
| static IntFlag sourceDrive( | ||||
|   | ||||
| @@ -1,140 +0,0 @@ | ||||
| #include "globals.h" | ||||
| #include "fluxmap.h" | ||||
| #include "sql.h" | ||||
| #include "bytes.h" | ||||
| #include "protocol.h" | ||||
| #include "fmt/format.h" | ||||
| #include "scp.h" | ||||
| #include <fstream> | ||||
|  | ||||
| static std::ifstream inputFile; | ||||
| static sqlite3* outputDb; | ||||
| static ScpHeader header; | ||||
| static nanoseconds_t resolution; | ||||
| static int startSide; | ||||
| static int endSide; | ||||
|  | ||||
| static void syntax() | ||||
| { | ||||
|     std::cout << "Syntax: fluxengine convert scptoflux <scpfile> <fluxfile>\n"; | ||||
|     exit(0); | ||||
| } | ||||
|  | ||||
| static void check_for_error() | ||||
| { | ||||
|     if (inputFile.fail()) | ||||
|         Error() << fmt::format("I/O error: {}", strerror(errno)); | ||||
| } | ||||
|  | ||||
| static int trackno(int strack) | ||||
| { | ||||
|     return strack >> 1; | ||||
| } | ||||
|  | ||||
| static int headno(int strack) | ||||
| { | ||||
|     return strack & 1; | ||||
| } | ||||
|  | ||||
| static void read_header() | ||||
| { | ||||
|     inputFile.read((char*) &header, sizeof(header)); | ||||
|     check_for_error(); | ||||
|  | ||||
|     if ((header.file_id[0] != 'S') | ||||
|             || (header.file_id[1] != 'C') | ||||
|             || (header.file_id[2] != 'P')) | ||||
|         Error() << "input not a SCP file"; | ||||
|  | ||||
|     resolution = 25 * (header.resolution + 1); | ||||
|     startSide = (header.heads == 2) ? 1 : 0; | ||||
|     endSide = (header.heads == 1) ? 0 : 1; | ||||
|  | ||||
|     if ((header.cell_width != 0) && (header.cell_width != 16)) | ||||
|         Error() << "currently only 16-bit cells are supported"; | ||||
|  | ||||
|     std::cout << fmt::format("tracks {}-{}, heads {}-{}\n", | ||||
|         trackno(header.start_track), trackno(header.end_track), startSide, endSide); | ||||
|     std::cout << fmt::format("sample resolution: {} ns\n", resolution); | ||||
| } | ||||
|  | ||||
| static void read_track(int strack) | ||||
| { | ||||
|     uint32_t offset = Bytes(header.track[strack], 4).reader().read_le32(); | ||||
| 	if (offset == 0) | ||||
| 		return; | ||||
|  | ||||
|     ScpTrack trackheader; | ||||
|     inputFile.seekg(offset, std::ios::beg); | ||||
|     inputFile.read((char*) &trackheader, sizeof(trackheader)); | ||||
|     check_for_error(); | ||||
|  | ||||
|     if ((trackheader.track_id[0] != 'T') | ||||
|             || (trackheader.track_id[1] != 'R') | ||||
|             || (trackheader.track_id[2] != 'K')) | ||||
|         Error() << "corrupt SCP file"; | ||||
|  | ||||
|     std::cout << fmt::format("{}.{}: ", trackno(strack), headno(strack)) | ||||
|         << std::flush; | ||||
|  | ||||
|     Fluxmap fluxmap; | ||||
|     nanoseconds_t pending = 0; | ||||
|     unsigned inputBytes = 0; | ||||
|     for (int revolution = 0; revolution < header.revolutions; revolution++) | ||||
|     { | ||||
|         if (revolution != 0) | ||||
|             fluxmap.appendIndex(); | ||||
|  | ||||
|         uint32_t datalength = Bytes(trackheader.revolution[revolution].length, 4).reader().read_le32(); | ||||
|         uint32_t dataoffset = Bytes(trackheader.revolution[revolution].offset, 4).reader().read_le32(); | ||||
|  | ||||
|         Bytes data(datalength*2); | ||||
|         inputFile.seekg(dataoffset + offset, std::ios::beg); | ||||
|         inputFile.read((char*) data.begin(), data.size()); | ||||
|         check_for_error(); | ||||
|  | ||||
|         ByteReader br(data); | ||||
|         for (int cell = 0; cell < datalength; cell++) | ||||
|         { | ||||
|             uint16_t interval = br.read_be16(); | ||||
|             if (interval) | ||||
|             { | ||||
|                 fluxmap.appendInterval((interval + pending) * resolution / NS_PER_TICK); | ||||
|                 fluxmap.appendPulse(); | ||||
|                 pending = 0; | ||||
|             } | ||||
|             else | ||||
|                 pending += 0x10000; | ||||
|         } | ||||
|  | ||||
|         inputBytes += datalength*2; | ||||
|     } | ||||
|  | ||||
|     std::cout << fmt::format(" {:.3f} ms in {} input bytes and {} output bytes\n", | ||||
|         fluxmap.duration() / 1e6, inputBytes, fluxmap.bytes()); | ||||
|     sqlWriteFlux(outputDb, trackno(strack), headno(strack), fluxmap); | ||||
| } | ||||
|  | ||||
| int mainConvertScpToFlux(int argc, const char* argv[]) | ||||
| { | ||||
|     if (argc != 3) | ||||
|         syntax(); | ||||
|      | ||||
|     inputFile.open(argv[1], std::ios::in | std::ios::binary); | ||||
|     if (!inputFile.is_open()) | ||||
| 		Error() << fmt::format("cannot open input file '{}'", argv[1]); | ||||
|  | ||||
|     outputDb = sqlOpen(argv[2], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); | ||||
|     sqlPrepareFlux(outputDb); | ||||
|     sqlWriteIntProperty(outputDb, "version", FLUX_VERSION_CURRENT); | ||||
|     sqlStmt(outputDb, "BEGIN;"); | ||||
|  | ||||
|     read_header(); | ||||
|     inputFile.seekg(sizeof(header), std::ios::beg); | ||||
|     for (unsigned i=header.start_track; i<=header.end_track; i++) | ||||
|         read_track(i); | ||||
|  | ||||
|     sqlStmt(outputDb, "COMMIT;"); | ||||
|     sqlClose(outputDb); | ||||
|     return 0; | ||||
| } | ||||
| @@ -47,7 +47,6 @@ static std::vector<Command> commands = | ||||
| static std::vector<Command> convertables = | ||||
| { | ||||
|     { "cwftoflux",     mainConvertCwfToFlux, "Converts CatWeasel stream files to flux.", }, | ||||
|     { "scptoflux",     mainConvertScpToFlux, "Converts Supercard Pro stream files to flux.", }, | ||||
| 	{ "image",         mainConvertImage,     "Converts one disk image to another.", }, | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user