mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
			FluxEngine
			...
			FluxEngine
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 10d385375f | ||
|  | 2f72c3f8f0 | ||
|  | 54edff9b94 | ||
|  | 112377f885 | ||
|  | 87e29fc386 | ||
|  | b1db5c48b1 | ||
|  | 38fab7edcb | ||
|  | d8172154c3 | ||
|  | eb924780ab | ||
|  | 28e0ef0463 | ||
|  | 4b07c38782 | 
| @@ -3,6 +3,10 @@ streams | ||||
| .*\.flux | ||||
| .*\.img | ||||
| .*\.raw | ||||
| .*\.orig | ||||
| .vscode | ||||
| remote | ||||
| FluxEngine.cydsn/CortexM3 | ||||
| FluxEngine.cydsn/Generated_Source | ||||
| FluxEngine.cydsn/codegentemp | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -22,7 +22,7 @@ export LIBS = $(shell pkg-config --libs $(PACKAGES)) | ||||
| export EXTENSION = | ||||
| endif | ||||
|  | ||||
| CFLAGS += -Ilib -Idep/fmt | ||||
| CFLAGS += -Ilib -Idep/fmt -Iarch | ||||
|  | ||||
| export OBJDIR = .obj | ||||
|  | ||||
|   | ||||
| @@ -92,6 +92,8 @@ void AmigaDecoder::decodeSectorRecord() | ||||
| 
 | ||||
|     uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024)); | ||||
|     _sector->data = deinterleave(ptr, 512); | ||||
| 
 | ||||
|     _sector->data.clear(); | ||||
|     _sector->data.writer().append(deinterleave(ptr, 512)).append(recoveryinfo); | ||||
|     _sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| } | ||||
| @@ -180,5 +180,7 @@ void MacintoshDecoder::decodeDataRecord() | ||||
|         inputbuffer[i] = decode_data_gcr(inputbuffer[i]); | ||||
|          | ||||
|     _sector->status = Sector::BAD_CHECKSUM; | ||||
|     _sector->data = decode_crazy_data(inputbuffer, _sector->status); | ||||
|     Bytes userData = decode_crazy_data(inputbuffer, _sector->status); | ||||
|     _sector->data.clear(); | ||||
|     _sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12)); | ||||
| } | ||||
| @@ -23,6 +23,17 @@ You should end up with an `amiga.adf` which is 901120 bytes long (for a | ||||
| normal DD disk) --- it ought to be a perfectly normal ADF file which you can | ||||
| use in an emulator. | ||||
|  | ||||
| If you want the metadata as well, specify a 528 byte sector size for the | ||||
| output image: | ||||
|  | ||||
| ``` | ||||
| fluxengine read amiga -o amiga.adf:b=528 | ||||
| ``` | ||||
|  | ||||
| You will end up with a 929280 byte long image which you probably _can't_ use | ||||
| in an emulator; each sector will contain the 512 bytes of user payload | ||||
| followed by the 16 bytes of metadata. | ||||
|  | ||||
| Useful references | ||||
| ----------------- | ||||
|  | ||||
|   | ||||
| @@ -50,6 +50,10 @@ with holes where missing sectors should be. This was easiest. If anyone can | ||||
| suggest a better way, please [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new). | ||||
|  | ||||
| The 12 bytes of metadata _follow_ the 512 bytes of user payload in the sector | ||||
| image. If you don't want it, specify a geometry in the output file with a | ||||
| 512-byte sectore size like `-o mac.img:c=80:h=1:s=12:b=512`. | ||||
|  | ||||
| Useful references | ||||
| ----------------- | ||||
|  | ||||
|   | ||||
							
								
								
									
										17
									
								
								doc/using.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								doc/using.md
									
									
									
									
									
								
							| @@ -112,6 +112,23 @@ sensible for the command you're using. | ||||
| **Important note:** FluxEngine _always_ uses zero-based units (even if the | ||||
| *disk format says otherwise). | ||||
|  | ||||
| ### Input and output specifiers | ||||
|  | ||||
| These use a very similar syntax to the source and destination specifiers | ||||
| (because they're based on the same microformat library!) but are used for | ||||
| input and output _images_: i.e. nicely lined up arrays of sectors which you | ||||
| can actually do something with. | ||||
|  | ||||
| Use `--input` (`-i`) or `--output` (`-o`) as appropriate to tell FluxEngine | ||||
| where you want to read from or write to. The actual format is autodetected | ||||
| based on the extension: | ||||
|  | ||||
|   - `.img` or `.adf`: raw sector images in CHS order. Append | ||||
|     `:c=80:h=2:s=9:b=512` to set the geometry; that specifies 80 cylinders, 2 | ||||
|     heads, 9 sectors, 512 bytes per sector. For output files (`--output`) the | ||||
|     geometry will be autodetected if left unspecified. For input files you | ||||
|     normally have to specify it. | ||||
|  | ||||
| ### High density disks | ||||
|  | ||||
| High density disks use a different magnetic medium to low and double density | ||||
|   | ||||
| @@ -5,6 +5,11 @@ | ||||
| #include <regex> | ||||
| #include <sstream> | ||||
|  | ||||
| MissingModifierException::MissingModifierException(const std::string& mod): | ||||
|     mod(mod), | ||||
|     std::runtime_error(fmt::format("missing mandatory modifier '{}'", mod)) | ||||
| {} | ||||
|  | ||||
| std::vector<std::string> DataSpec::split( | ||||
|         const std::string& s, const std::string& delimiter) | ||||
| { | ||||
| @@ -74,31 +79,108 @@ void DataSpec::set(const std::string& spec) | ||||
|     filename = words[0]; | ||||
|     if (words.size() > 1) | ||||
|     { | ||||
|         locations.clear(); | ||||
|  | ||||
|         for (size_t i = 1; i < words.size(); i++) | ||||
|         { | ||||
|             auto mod = parseMod(words[i]); | ||||
|             if ((mod.name != "t") && (mod.name != "s") && (mod.name != "d")) | ||||
|                 Error() << fmt::format("unknown data modifier '{}'", mod.name); | ||||
|             modifiers[mod.name] = mod; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|         const auto& drives = modifiers["d"].data; | ||||
| const DataSpec::Modifier& DataSpec::at(const std::string& mod) const | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         return modifiers.at(mod); | ||||
|     } | ||||
|     catch (const std::out_of_range& e) | ||||
|     { | ||||
|         throw MissingModifierException(mod); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool DataSpec::has(const std::string& mod) const | ||||
| { | ||||
|     return modifiers.find(mod) != modifiers.end(); | ||||
| } | ||||
|  | ||||
| FluxSpec::FluxSpec(const DataSpec& spec) | ||||
| { | ||||
|     try  | ||||
|     { | ||||
|         filename = spec.filename; | ||||
|  | ||||
|         locations.clear(); | ||||
|  | ||||
|         const auto& drives = spec.at("d").data; | ||||
|         if (drives.size() != 1) | ||||
|             Error() << "you must specify exactly one drive"; | ||||
|         drive = *drives.begin(); | ||||
|  | ||||
|         const auto& tracks = modifiers["t"].data; | ||||
|         const auto& sides = modifiers["s"].data; | ||||
|         const auto& tracks = spec.at("t").data; | ||||
|         const auto& sides = spec.at("s").data; | ||||
|         for (auto track : tracks) | ||||
|         { | ||||
|             for (auto side : sides) | ||||
|                 locations.push_back({ drive, track, side }); | ||||
|         } | ||||
|  | ||||
|         for (const auto& e : spec.modifiers) | ||||
|         { | ||||
|             const auto name = e.second.name; | ||||
|             if ((name != "t") && (name != "s") && (name != "d")) | ||||
|                 Error() << fmt::format("unknown fluxspec modifier '{}'", name); | ||||
|         } | ||||
|     } | ||||
|     catch (const MissingModifierException& e) | ||||
|     { | ||||
|         Error() << e.what() << " in fluxspec '" << spec << "'"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ImageSpec::ImageSpec(const DataSpec& spec) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         filename = spec.filename; | ||||
|  | ||||
|         if (!spec.has("c") && !spec.has("h") && !spec.has("s") && !spec.has("b")) | ||||
|         { | ||||
|             cylinders = heads = sectors = bytes = 0; | ||||
|             initialised = false; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             cylinders = spec.at("c").only(); | ||||
|             heads = spec.at("h").only(); | ||||
|             sectors = spec.at("s").only(); | ||||
|             bytes = spec.at("b").only(); | ||||
|             initialised = true; | ||||
|         } | ||||
|     } | ||||
|     catch (const MissingModifierException& e) | ||||
|     { | ||||
|         Error() << e.what() << " in imagespec '" << spec << "'"; | ||||
|     } | ||||
|  | ||||
|     for (const auto& e : spec.modifiers) | ||||
|     { | ||||
|         const auto name = e.second.name; | ||||
|         if ((name != "c") && (name != "h") && (name != "s") && (name != "b")) | ||||
|             Error() << fmt::format("unknown fluxspec modifier '{}'", name); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ImageSpec::ImageSpec(const std::string filename, | ||||
|         unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes): | ||||
|     filename(filename), | ||||
|     cylinders(cylinders), | ||||
|     heads(heads), | ||||
|     sectors(sectors), | ||||
|     bytes(bytes), | ||||
|     initialised(true) | ||||
| {} | ||||
|  | ||||
| DataSpec::operator std::string(void) const | ||||
| { | ||||
|     std::stringstream ss; | ||||
|   | ||||
| @@ -1,8 +1,57 @@ | ||||
| #ifndef DATASPEC_H | ||||
| #define DATASPEC_H | ||||
|  | ||||
| class MissingModifierException : public std::runtime_error | ||||
| { | ||||
| public: | ||||
|     MissingModifierException(const std::string& mod); | ||||
|     const std::string mod; | ||||
| }; | ||||
|  | ||||
| class DataSpec | ||||
| { | ||||
| public: | ||||
|     struct Modifier | ||||
|     { | ||||
|         std::string name; | ||||
|         std::set<unsigned> data; | ||||
|         std::string source; | ||||
|  | ||||
|         bool operator == (const Modifier& other) const | ||||
|         { return (name == other.name) && (data == other.data); } | ||||
|  | ||||
|         bool operator != (const Modifier& other) const | ||||
|         { return (name != other.name) || (data != other.data); } | ||||
|  | ||||
|         unsigned only() const | ||||
|         { | ||||
|             if (data.size() != 1) | ||||
|                 Error() << "modifier " << name << " can only have one value"; | ||||
|             return *(data.begin()); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| public: | ||||
|     static std::vector<std::string> split( | ||||
|         const std::string& s, const std::string& delimiter); | ||||
|     static Modifier parseMod(const std::string& spec); | ||||
|  | ||||
| public: | ||||
|     DataSpec(const std::string& spec) | ||||
|     { set(spec); } | ||||
|  | ||||
|     void set(const std::string& spec); | ||||
|     operator std::string () const; | ||||
|  | ||||
|     const Modifier& at(const std::string& mod) const; | ||||
|     bool has(const std::string& mod) const; | ||||
|  | ||||
|     std::string filename; | ||||
|     std::map<std::string, Modifier> modifiers; | ||||
| }; | ||||
|  | ||||
| class FluxSpec | ||||
| { | ||||
| public: | ||||
|     struct Location | ||||
|     { | ||||
| @@ -17,36 +66,29 @@ public: | ||||
|         { return (drive != other.drive) || (track != other.track) || (side != other.side); } | ||||
|     }; | ||||
|  | ||||
|     struct Modifier | ||||
|     { | ||||
|         std::string name; | ||||
|         std::set<unsigned> data; | ||||
|         std::string source; | ||||
|  | ||||
|         bool operator == (const Modifier& other) const | ||||
|         { return (name == other.name) && (data == other.data); } | ||||
|  | ||||
|         bool operator != (const Modifier& other) const | ||||
|         { return (name != other.name) || (data != other.data); } | ||||
|     }; | ||||
| public: | ||||
|     FluxSpec(const DataSpec& dataspec); | ||||
|  | ||||
| public: | ||||
|     static std::vector<std::string> split( | ||||
|         const std::string& s, const std::string& delimiter); | ||||
|     static Modifier parseMod(const std::string& spec); | ||||
|  | ||||
| public: | ||||
|     DataSpec(const std::string& spec) | ||||
|     { set(spec); } | ||||
|  | ||||
|     void set(const std::string& spec); | ||||
|     operator std::string () const; | ||||
|  | ||||
|     std::string filename; | ||||
|     std::map<std::string, Modifier> modifiers; | ||||
|     std::vector<Location> locations; | ||||
|     unsigned drive; | ||||
|     unsigned revolutions; | ||||
| }; | ||||
|  | ||||
| class ImageSpec | ||||
| { | ||||
| public: | ||||
|     ImageSpec(const DataSpec& dataspec); | ||||
|     ImageSpec(const std::string filename, | ||||
|         unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes); | ||||
|  | ||||
| public: | ||||
|     std::string filename; | ||||
|     unsigned cylinders; | ||||
|     unsigned heads; | ||||
|     unsigned sectors; | ||||
|     unsigned bytes; | ||||
|     bool initialised : 1; | ||||
| }; | ||||
|  | ||||
| static inline std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending) | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<FluxSink> FluxSink::create(const DataSpec& spec) | ||||
| std::unique_ptr<FluxSink> FluxSink::create(const FluxSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #define FLUXSINK_H | ||||
|  | ||||
| class Fluxmap; | ||||
| class DataSpec; | ||||
| class FluxSpec; | ||||
|  | ||||
| class FluxSink | ||||
| { | ||||
| @@ -14,7 +14,7 @@ private: | ||||
|     static std::unique_ptr<FluxSink> createHardwareFluxSink(unsigned drive); | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<FluxSink> create(const DataSpec& spec); | ||||
|     static std::unique_ptr<FluxSink> create(const FluxSpec& spec); | ||||
|  | ||||
| public: | ||||
|     virtual void writeFlux(int track, int side, Fluxmap& fluxmap) = 0; | ||||
|   | ||||
| @@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending) | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<FluxSource> FluxSource::create(const DataSpec& spec) | ||||
| std::unique_ptr<FluxSource> FluxSource::create(const FluxSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| extern FlagGroup hardwareFluxSourceFlags; | ||||
|  | ||||
| class Fluxmap; | ||||
| class DataSpec; | ||||
| class FluxSpec; | ||||
|  | ||||
| class FluxSource | ||||
| { | ||||
| @@ -19,7 +19,7 @@ private: | ||||
|     static std::unique_ptr<FluxSource> createStreamFluxSource(const std::string& path); | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<FluxSource> create(const DataSpec& spec); | ||||
|     static std::unique_ptr<FluxSource> create(const FluxSpec& spec); | ||||
|  | ||||
| public: | ||||
|     virtual std::unique_ptr<Fluxmap> readFlux(int track, int side) = 0; | ||||
|   | ||||
							
								
								
									
										147
									
								
								lib/image.cc
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								lib/image.cc
									
									
									
									
									
								
							| @@ -1,150 +1,25 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "imagereader/imagereader.h" | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
| #include <algorithm> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
|  | ||||
| Geometry guessGeometry(const SectorSet& sectors) | ||||
| SectorSet readSectorsFromFile(const ImageSpec& spec) | ||||
| { | ||||
| 	Geometry g; | ||||
| 	sectors.calculateSize(g.tracks, g.heads, g.sectors, g.sectorSize); | ||||
| 	return g; | ||||
| 	return ImageReader::create(spec)->readImage(); | ||||
| } | ||||
|  | ||||
| void readSectorsFromFile(SectorSet& sectors, const Geometry& geometry, | ||||
| 		const std::string& filename) | ||||
| void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
|     std::ifstream inputFile(filename, std::ios::in | std::ios::binary); | ||||
|     if (!inputFile.is_open()) | ||||
| 		Error() << "cannot open input file"; | ||||
|  | ||||
|     size_t headSize = geometry.sectors * geometry.sectorSize; | ||||
|     size_t trackSize = headSize * geometry.heads; | ||||
|  | ||||
|     std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total", | ||||
| 					geometry.tracks, geometry.heads, | ||||
| 					geometry.sectors, geometry.sectorSize, | ||||
| 					geometry.tracks * trackSize / 1024) | ||||
| 			  << std::endl; | ||||
|  | ||||
| 	for (int track = 0; track < geometry.tracks; track++) | ||||
| 	{ | ||||
| 		for (int head = 0; head < geometry.heads; head++) | ||||
| 		{ | ||||
| 			for (int sectorId = 0; sectorId < geometry.sectors; sectorId++) | ||||
| 			{ | ||||
| 				inputFile.seekg(track*trackSize + head*headSize + sectorId*geometry.sectorSize, std::ios::beg); | ||||
|  | ||||
| 				Bytes data(geometry.sectorSize); | ||||
| 				inputFile.read((char*) data.begin(), geometry.sectorSize); | ||||
|  | ||||
| 				std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId); | ||||
| 				sector.reset(new Sector); | ||||
| 				sector->status = Sector::OK; | ||||
| 				sector->logicalTrack = sector->physicalTrack = track; | ||||
| 				sector->logicalSide = sector->physicalSide = head; | ||||
| 				sector->logicalSector = sectorId; | ||||
| 				sector->data = data; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void writeSectorsToFile(const SectorSet& sectors, const Geometry& geometry, | ||||
| 		const std::string& filename) | ||||
| { | ||||
| 	/* Emit the map. */ | ||||
|  | ||||
| 	int badSectors = 0; | ||||
| 	int missingSectors = 0; | ||||
| 	int totalSectors = 0; | ||||
| 	std::cout << "H.SS Tracks --->" << std::endl; | ||||
| 	for (int head = 0; head < geometry.heads; head++) | ||||
| 	{ | ||||
| 		for (int sectorId = 0; sectorId < geometry.sectors; sectorId++) | ||||
| 		{ | ||||
| 			std::cout << fmt::format("{}.{:2} ", head, sectorId); | ||||
| 			for (int track = 0; track < geometry.tracks; track++) | ||||
| 			{ | ||||
| 				Sector* sector = sectors.get(track, head, sectorId); | ||||
| 				if (!sector) | ||||
| 				{ | ||||
| 					std::cout << 'X'; | ||||
| 					missingSectors++; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					switch (sector->status) | ||||
| 					{ | ||||
| 						case Sector::OK: | ||||
|                             std::cout << '.'; | ||||
|                             break; | ||||
|  | ||||
|                         case Sector::BAD_CHECKSUM: | ||||
|                             std::cout << 'B'; | ||||
|                             badSectors++; | ||||
|                             break; | ||||
|  | ||||
|                         case Sector::CONFLICT: | ||||
|                             std::cout << 'C'; | ||||
|                             badSectors++; | ||||
|                             break; | ||||
|  | ||||
|                         default: | ||||
|                             std::cout << '?'; | ||||
|                             break; | ||||
|                     } | ||||
| 				} | ||||
| 				totalSectors++; | ||||
| 			} | ||||
| 			std::cout << std::endl; | ||||
| 		} | ||||
| 	} | ||||
| 	int goodSectors = totalSectors - missingSectors - badSectors; | ||||
| 	if (totalSectors == 0) | ||||
| 		std::cout << "No sectors in output; skipping analysis" << std::endl; | ||||
| 	else | ||||
| 	{ | ||||
| 		std::cout << "Good sectors: " << goodSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*goodSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
| 		std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*missingSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
| 		std::cout << "Bad sectors: " << badSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*badSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
|     } | ||||
|  | ||||
|     size_t headSize = geometry.sectors * geometry.sectorSize; | ||||
|     size_t trackSize = headSize * geometry.heads; | ||||
|  | ||||
|     std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total", | ||||
| 					geometry.tracks, geometry.heads, | ||||
| 					geometry.sectors, geometry.sectorSize, | ||||
| 					geometry.tracks * trackSize / 1024) | ||||
| 			  << std::endl; | ||||
|  | ||||
|     std::ofstream outputFile(filename, std::ios::out | std::ios::binary); | ||||
|     if (!outputFile.is_open()) | ||||
| 		Error() << "cannot open output file"; | ||||
|  | ||||
| 	for (int track = 0; track < geometry.tracks; track++) | ||||
| 	{ | ||||
| 		for (int head = 0; head < geometry.heads; head++) | ||||
| 		{ | ||||
| 			for (int sectorId = 0; sectorId < geometry.sectors; sectorId++) | ||||
| 			{ | ||||
| 				auto sector = sectors.get(track, head, sectorId); | ||||
| 				if (sector) | ||||
| 				{ | ||||
| 					outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg); | ||||
| 					outputFile.write((const char*) sector->data.cbegin(), sector->data.size()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec)); | ||||
| 	writer->adjustGeometry(); | ||||
| 	writer->printMap(); | ||||
| 	writer->writeImage(); | ||||
| } | ||||
|   | ||||
							
								
								
									
										21
									
								
								lib/image.h
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								lib/image.h
									
									
									
									
									
								
							| @@ -2,26 +2,13 @@ | ||||
| #define IMAGE_H | ||||
|  | ||||
| class SectorSet; | ||||
| class ImageSpec; | ||||
|  | ||||
| class Geometry | ||||
| { | ||||
| public: | ||||
| 	int tracks; | ||||
| 	int heads; | ||||
| 	int sectors; | ||||
| 	int sectorSize; | ||||
| }; | ||||
|  | ||||
| extern Geometry guessGeometry(const SectorSet& sectors); | ||||
|  | ||||
| extern void readSectorsFromFile( | ||||
| 	SectorSet& sectors, | ||||
| 	const Geometry& geometry, | ||||
| 	const std::string& filename); | ||||
| extern SectorSet readSectorsFromFile( | ||||
| 	const ImageSpec& filename); | ||||
|  | ||||
| extern void writeSectorsToFile( | ||||
| 	const SectorSet& sectors, | ||||
| 	const Geometry& geometry, | ||||
| 	const std::string& filename); | ||||
| 	const ImageSpec& filename); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										31
									
								
								lib/imagereader/imagereader.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/imagereader/imagereader.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "imagereader/imagereader.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| static bool ends_with(const std::string& value, const std::string& ending) | ||||
| { | ||||
|     if (ending.size() > value.size()) | ||||
|         return false; | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|     if (ends_with(filename, ".img") || ends_with(filename, ".adf")) | ||||
|         return createImgImageReader(spec); | ||||
|  | ||||
|     Error() << "unrecognised image filename extension"; | ||||
|     return std::unique_ptr<ImageReader>(); | ||||
| } | ||||
|  | ||||
| ImageReader::ImageReader(const ImageSpec& spec): | ||||
|     spec(spec) | ||||
| {} | ||||
|  | ||||
							
								
								
									
										27
									
								
								lib/imagereader/imagereader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/imagereader/imagereader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #ifndef IMAGEREADER_H | ||||
| #define IMAGEREADER_H | ||||
|  | ||||
| class SectorSet; | ||||
| class ImageSpec; | ||||
|  | ||||
| class ImageReader | ||||
| { | ||||
| public: | ||||
| 	ImageReader(const ImageSpec& spec); | ||||
| 	virtual ~ImageReader() {}; | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<ImageReader> create(const ImageSpec& spec); | ||||
|  | ||||
| private: | ||||
|     static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec); | ||||
|  | ||||
| public: | ||||
| 	virtual SectorSet readImage() = 0; | ||||
|  | ||||
| protected: | ||||
| 	ImageSpec spec; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										66
									
								
								lib/imagereader/imgimagereader.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								lib/imagereader/imgimagereader.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "imagereader/imagereader.h" | ||||
| #include "fmt/format.h" | ||||
| #include <algorithm> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
|  | ||||
| class ImgImageReader : public ImageReader | ||||
| { | ||||
| public: | ||||
| 	ImgImageReader(const ImageSpec& spec): | ||||
| 		ImageReader(spec) | ||||
| 	{} | ||||
|  | ||||
| 	SectorSet readImage() | ||||
| 	{ | ||||
|         std::ifstream inputFile(spec.filename, std::ios::in | std::ios::binary); | ||||
|         if (!inputFile.is_open()) | ||||
|             Error() << "cannot open input file"; | ||||
|  | ||||
|         size_t headSize = spec.sectors * spec.bytes; | ||||
|         size_t trackSize = headSize * spec.heads; | ||||
|  | ||||
|         std::cout << fmt::format("reading {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total", | ||||
|                         spec.cylinders, spec.heads, | ||||
|                         spec.sectors, spec.bytes, | ||||
|                         spec.cylinders * trackSize / 1024) | ||||
|                 << std::endl; | ||||
|  | ||||
|         SectorSet sectors; | ||||
|         for (int track = 0; track < spec.cylinders; track++) | ||||
|         { | ||||
|             for (int head = 0; head < spec.heads; head++) | ||||
|             { | ||||
|                 for (int sectorId = 0; sectorId < spec.sectors; sectorId++) | ||||
|                 { | ||||
|                     inputFile.seekg(track*trackSize + head*headSize + sectorId*spec.bytes, std::ios::beg); | ||||
|  | ||||
|                     Bytes data(spec.bytes); | ||||
|                     inputFile.read((char*) data.begin(), spec.bytes); | ||||
|  | ||||
|                     std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId); | ||||
|                     sector.reset(new Sector); | ||||
|                     sector->status = Sector::OK; | ||||
|                     sector->logicalTrack = sector->physicalTrack = track; | ||||
|                     sector->logicalSide = sector->physicalSide = head; | ||||
|                     sector->logicalSector = sectorId; | ||||
|                     sector->data = data; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return sectors; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<ImageReader> ImageReader::createImgImageReader( | ||||
| 	const ImageSpec& spec) | ||||
| { | ||||
|     return std::unique_ptr<ImageReader>(new ImgImageReader(spec)); | ||||
| } | ||||
|  | ||||
							
								
								
									
										105
									
								
								lib/imagewriter/imagewriter.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								lib/imagewriter/imagewriter.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| static bool ends_with(const std::string& value, const std::string& ending) | ||||
| { | ||||
|     if (ending.size() > value.size()) | ||||
|         return false; | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|     if (ends_with(filename, ".img") || ends_with(filename, ".adf")) | ||||
|         return createImgImageWriter(sectors, spec); | ||||
|  | ||||
|     Error() << "unrecognised image filename extension"; | ||||
|     return std::unique_ptr<ImageWriter>(); | ||||
| } | ||||
|  | ||||
| ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec): | ||||
|     sectors(sectors), | ||||
|     spec(spec) | ||||
| {} | ||||
|  | ||||
| void ImageWriter::adjustGeometry() | ||||
| { | ||||
| 	if (!spec.initialised) | ||||
| 	{ | ||||
| 		sectors.calculateSize(spec.cylinders, spec.heads, spec.sectors, spec.bytes); | ||||
|         spec.initialised = true; | ||||
| 		std::cout << "Autodetecting output geometry\n"; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ImageWriter::printMap() | ||||
| { | ||||
| 	int badSectors = 0; | ||||
| 	int missingSectors = 0; | ||||
| 	int totalSectors = 0; | ||||
| 	std::cout << "H.SS Tracks --->" << std::endl; | ||||
| 	for (int head = 0; head < spec.heads; head++) | ||||
| 	{ | ||||
| 		for (int sectorId = 0; sectorId < spec.sectors; sectorId++) | ||||
| 		{ | ||||
| 			std::cout << fmt::format("{}.{:2} ", head, sectorId); | ||||
| 			for (int track = 0; track < spec.cylinders; track++) | ||||
| 			{ | ||||
| 				const auto& sector = sectors.get(track, head, sectorId); | ||||
| 				if (!sector) | ||||
| 				{ | ||||
| 					std::cout << 'X'; | ||||
| 					missingSectors++; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					switch (sector->status) | ||||
| 					{ | ||||
| 						case Sector::OK: | ||||
|                             std::cout << '.'; | ||||
|                             break; | ||||
|  | ||||
|                         case Sector::BAD_CHECKSUM: | ||||
|                             std::cout << 'B'; | ||||
|                             badSectors++; | ||||
|                             break; | ||||
|  | ||||
|                         case Sector::CONFLICT: | ||||
|                             std::cout << 'C'; | ||||
|                             badSectors++; | ||||
|                             break; | ||||
|  | ||||
|                         default: | ||||
|                             std::cout << '?'; | ||||
|                             break; | ||||
|                     } | ||||
| 				} | ||||
| 				totalSectors++; | ||||
| 			} | ||||
| 			std::cout << std::endl; | ||||
| 		} | ||||
| 	} | ||||
| 	int goodSectors = totalSectors - missingSectors - badSectors; | ||||
| 	if (totalSectors == 0) | ||||
| 		std::cout << "No sectors in output; skipping analysis" << std::endl; | ||||
| 	else | ||||
| 	{ | ||||
| 		std::cout << "Good sectors: " << goodSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*goodSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
| 		std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*missingSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
| 		std::cout << "Bad sectors: " << badSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*badSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										30
									
								
								lib/imagewriter/imagewriter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/imagewriter/imagewriter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| #ifndef IMAGEWRITER_H | ||||
| #define IMAGEWRITER_H | ||||
|  | ||||
| class SectorSet; | ||||
| class ImageSpec; | ||||
|  | ||||
| class ImageWriter | ||||
| { | ||||
| public: | ||||
| 	ImageWriter(const SectorSet& sectors, const ImageSpec& spec); | ||||
| 	virtual ~ImageWriter() {}; | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<ImageWriter> create(const SectorSet& sectors, const ImageSpec& spec); | ||||
|  | ||||
| private: | ||||
|     static std::unique_ptr<ImageWriter> createImgImageWriter( | ||||
| 		const SectorSet& sectors, const ImageSpec& spec); | ||||
|  | ||||
| public: | ||||
| 	virtual void adjustGeometry(); | ||||
| 	void printMap(); | ||||
| 	virtual void writeImage() = 0; | ||||
|  | ||||
| protected: | ||||
| 	const SectorSet& sectors; | ||||
| 	ImageSpec spec; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										62
									
								
								lib/imagewriter/imgimagewriter.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								lib/imagewriter/imgimagewriter.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
| #include <algorithm> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
|  | ||||
| class ImgImageWriter : public ImageWriter | ||||
| { | ||||
| public: | ||||
| 	ImgImageWriter(const SectorSet& sectors, const ImageSpec& spec): | ||||
| 		ImageWriter(sectors, spec) | ||||
| 	{} | ||||
|  | ||||
| 	void writeImage() | ||||
| 	{ | ||||
| 		unsigned numCylinders = spec.cylinders; | ||||
| 		unsigned numHeads = spec.heads; | ||||
| 		unsigned numSectors = spec.sectors; | ||||
| 		unsigned numBytes = spec.bytes; | ||||
|  | ||||
| 		size_t headSize = numSectors * numBytes; | ||||
| 		size_t trackSize = headSize * numHeads; | ||||
|  | ||||
| 		std::cout << fmt::format("writing {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total", | ||||
| 						numCylinders, numHeads, | ||||
| 						numSectors, numBytes, | ||||
| 						numCylinders * trackSize / 1024) | ||||
| 				<< std::endl; | ||||
|  | ||||
| 		std::ofstream outputFile(spec.filename, std::ios::out | std::ios::binary); | ||||
| 		if (!outputFile.is_open()) | ||||
| 			Error() << "cannot open output file"; | ||||
|  | ||||
| 		for (int track = 0; track < numCylinders; track++) | ||||
| 		{ | ||||
| 			for (int head = 0; head < numHeads; head++) | ||||
| 			{ | ||||
| 				for (int sectorId = 0; sectorId < numSectors; sectorId++) | ||||
| 				{ | ||||
| 					const auto& sector = sectors.get(track, head, sectorId); | ||||
| 					if (sector) | ||||
| 					{ | ||||
| 						outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*numBytes, std::ios::beg); | ||||
| 						outputFile.write((const char*) sector->data.cbegin(), sector->data.size()); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<ImageWriter> ImageWriter::createImgImageWriter( | ||||
| 	const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
|     return std::unique_ptr<ImageWriter>(new ImgImageWriter(sectors, spec)); | ||||
| } | ||||
| @@ -23,6 +23,11 @@ static DataSpecFlag source( | ||||
|     "source for data", | ||||
|     ":t=0-79:s=0-1:d=0"); | ||||
|  | ||||
| static DataSpecFlag output( | ||||
| 	{ "--output", "-o" }, | ||||
| 	"output image file to write to", | ||||
| 	""); | ||||
|  | ||||
| static StringFlag destination( | ||||
|     { "--write-flux", "-f" }, | ||||
|     "write the raw magnetic flux to this file", | ||||
| @@ -52,6 +57,11 @@ void setReaderDefaultSource(const std::string& source) | ||||
|     ::source.set(source); | ||||
| } | ||||
|  | ||||
| void setReaderDefaultOutput(const std::string& output) | ||||
| { | ||||
|     ::output.set(output); | ||||
| } | ||||
|  | ||||
| void setReaderRevolutions(int revolutions) | ||||
| { | ||||
| 	setHardwareFluxSourceRevolutions(revolutions); | ||||
| @@ -71,9 +81,9 @@ void Track::readFluxmap() | ||||
|  | ||||
| std::vector<std::unique_ptr<Track>> readTracks() | ||||
| { | ||||
|     const DataSpec& dataSpec = source; | ||||
|     const FluxSpec spec(source); | ||||
|  | ||||
|     std::cout << "Reading from: " << dataSpec << std::endl; | ||||
|     std::cout << "Reading from: " << source << std::endl; | ||||
|  | ||||
| 	setHardwareFluxSourceDensity(highDensityFlag); | ||||
|  | ||||
| @@ -92,10 +102,10 @@ std::vector<std::unique_ptr<Track>> readTracks() | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	std::shared_ptr<FluxSource> fluxSource = FluxSource::create(dataSpec); | ||||
| 	std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec); | ||||
|  | ||||
| 	std::vector<std::unique_ptr<Track>> tracks; | ||||
|     for (const auto& location : dataSpec.locations) | ||||
|     for (const auto& location : spec.locations) | ||||
| 	{ | ||||
| 		auto track = std::make_unique<Track>(location.track, location.side); | ||||
| 		track->fluxsource = fluxSource; | ||||
| @@ -140,8 +150,10 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename) | ||||
| void readDiskCommand(AbstractDecoder& decoder) | ||||
| { | ||||
| 	const ImageSpec outputSpec(output); | ||||
|  | ||||
| 	bool failures = false; | ||||
| 	SectorSet allSectors; | ||||
| 	auto tracks = readTracks(); | ||||
| @@ -239,8 +251,7 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename | ||||
|         std::cout << size << " bytes decoded." << std::endl; | ||||
|     } | ||||
|  | ||||
| 	Geometry geometry = guessGeometry(allSectors); | ||||
|     writeSectorsToFile(allSectors, geometry, outputFilename); | ||||
|     writeSectorsToFile(allSectors, outputSpec); | ||||
| 	if (failures) | ||||
| 		std::cerr << "Warning: some sectors could not be decoded." << std::endl; | ||||
| } | ||||
|   | ||||
| @@ -11,10 +11,11 @@ class Track; | ||||
| extern FlagGroup readerFlags; | ||||
|  | ||||
| extern void setReaderDefaultSource(const std::string& source); | ||||
| extern void setReaderDefaultOutput(const std::string& output); | ||||
| extern void setReaderRevolutions(int revolutions); | ||||
|  | ||||
| extern std::vector<std::unique_ptr<Track>> readTracks(); | ||||
|  | ||||
| extern void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename); | ||||
| extern void readDiskCommand(AbstractDecoder& decoder); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -18,8 +18,9 @@ Sector* SectorSet::get(int track, int head, int sector) const | ||||
| 	return i->second.get(); | ||||
| } | ||||
|  | ||||
| void SectorSet::calculateSize(int& numTracks, int& numHeads, int& numSectors, | ||||
| 	int& sectorSize) const | ||||
| void SectorSet::calculateSize( | ||||
| 	unsigned& numTracks, unsigned& numHeads, | ||||
| 	unsigned& numSectors, unsigned& sectorSize) const | ||||
| { | ||||
| 	numTracks = numHeads = numSectors = sectorSize = 0; | ||||
|  | ||||
| @@ -28,10 +29,10 @@ void SectorSet::calculateSize(int& numTracks, int& numHeads, int& numSectors, | ||||
| 		auto& sector = i.second; | ||||
| 		if (sector) | ||||
| 		{ | ||||
| 			numTracks = std::max(numTracks, sector->logicalTrack+1); | ||||
| 			numHeads = std::max(numHeads, sector->logicalSide+1); | ||||
| 			numSectors = std::max(numSectors, sector->logicalSector+1); | ||||
| 			sectorSize = std::max(sectorSize, (int)sector->data.size()); | ||||
| 			numTracks = std::max(numTracks, (unsigned)sector->logicalTrack+1); | ||||
| 			numHeads = std::max(numHeads, (unsigned)sector->logicalSide+1); | ||||
| 			numSectors = std::max(numSectors, (unsigned)sector->logicalSector+1); | ||||
| 			sectorSize = std::max(sectorSize, (unsigned)sector->data.size()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -17,8 +17,9 @@ public: | ||||
| 	std::unique_ptr<Sector>& get(int track, int head, int sector); | ||||
| 	Sector* get(int track, int head, int sector) const; | ||||
|  | ||||
| 	void calculateSize(int& numTracks, int& numHeads, int& numSectors, | ||||
| 		int& sectorSize) const; | ||||
| 	void calculateSize( | ||||
| 		unsigned& numTracks, unsigned& numHeads, unsigned& numSectors, | ||||
| 		unsigned& sectorSize) const; | ||||
|  | ||||
| private: | ||||
| 	std::map<const key_t, std::unique_ptr<Sector>> _data; | ||||
|   | ||||
| @@ -22,6 +22,11 @@ static DataSpecFlag dest( | ||||
|     "destination for data", | ||||
|     ":d=0:t=0-79:s=0-1"); | ||||
|  | ||||
| static DataSpecFlag input( | ||||
|     { "--input", "-i" }, | ||||
|     "input image file to read from", | ||||
|     ""); | ||||
|  | ||||
| static SettableFlag highDensityFlag( | ||||
| 	{ "--high-density", "-H" }, | ||||
| 	"set the drive to high density mode"); | ||||
| @@ -33,12 +38,17 @@ void setWriterDefaultDest(const std::string& dest) | ||||
|     ::dest.set(dest); | ||||
| } | ||||
|  | ||||
| void setWriterDefaultInput(const std::string& input) | ||||
| { | ||||
|     ::input.set(input); | ||||
| } | ||||
|  | ||||
| void writeTracks( | ||||
| 	const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer) | ||||
| { | ||||
|     const DataSpec& spec = dest; | ||||
|     const FluxSpec spec(dest); | ||||
|  | ||||
|     std::cout << "Writing to: " << spec << std::endl; | ||||
|     std::cout << "Writing to: " << dest << std::endl; | ||||
|  | ||||
| 	setHardwareFluxSourceDensity(highDensityFlag); | ||||
| 	setHardwareFluxSinkDensity(highDensityFlag); | ||||
| @@ -103,12 +113,10 @@ void fillBitmapTo(std::vector<bool>& bitmap, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void writeDiskCommand( | ||||
|     AbstractEncoder& encoder, const Geometry& geometry, const std::string& inputFilename) | ||||
| void writeDiskCommand(AbstractEncoder& encoder) | ||||
| { | ||||
|     SectorSet allSectors; | ||||
|  | ||||
| 	readSectorsFromFile(allSectors, geometry, inputFilename); | ||||
|     const ImageSpec spec(input); | ||||
| 	SectorSet allSectors = readSectorsFromFile(spec); | ||||
| 	writeTracks( | ||||
| 		[&](int track, int side) -> std::unique_ptr<Fluxmap> | ||||
| 		{ | ||||
|   | ||||
| @@ -10,6 +10,7 @@ class AbstractEncoder; | ||||
| class Geometry; | ||||
|  | ||||
| extern void setWriterDefaultDest(const std::string& dest); | ||||
| extern void setWriterDefaultInput(const std::string& input); | ||||
|  | ||||
| extern void writeTracks(const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer); | ||||
|  | ||||
| @@ -17,7 +18,6 @@ extern void fillBitmapTo(std::vector<bool>& bitmap, | ||||
| 		unsigned& cursor, unsigned terminateAt, | ||||
| 		const std::vector<bool>& pattern); | ||||
| 	 | ||||
| extern void writeDiskCommand( | ||||
|     AbstractEncoder& encoder, const Geometry& geometry, const std::string& inputFilename); | ||||
| extern void writeDiskCommand(AbstractEncoder& encoder); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										30
									
								
								mkninja.sh
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								mkninja.sh
									
									
									
									
									
								
							| @@ -137,13 +137,20 @@ buildlibrary libfmt.a \ | ||||
|     dep/fmt/posix.cc \ | ||||
|  | ||||
| buildlibrary libbackend.a \ | ||||
|     lib/aeslanier/decoder.cc \ | ||||
|     lib/amiga/decoder.cc \ | ||||
|     lib/apple2/decoder.cc \ | ||||
|     lib/brother/decoder.cc \ | ||||
|     lib/brother/encoder.cc \ | ||||
|     arch/aeslanier/decoder.cc \ | ||||
|     arch/amiga/decoder.cc \ | ||||
|     arch/apple2/decoder.cc \ | ||||
|     arch/brother/decoder.cc \ | ||||
|     arch/brother/encoder.cc \ | ||||
|     arch/c64/decoder.cc \ | ||||
|     arch/f85/decoder.cc \ | ||||
|     arch/fb100/decoder.cc \ | ||||
|     arch/ibm/decoder.cc \ | ||||
|     arch/macintosh/decoder.cc \ | ||||
|     arch/mx/decoder.cc \ | ||||
|     arch/victor9k/decoder.cc \ | ||||
|     arch/zilogmcz/decoder.cc \ | ||||
|     lib/bytes.cc \ | ||||
|     lib/c64/decoder.cc \ | ||||
|     lib/common/crunch.c \ | ||||
|     lib/crc.cc \ | ||||
|     lib/dataspec.cc \ | ||||
| @@ -151,8 +158,6 @@ buildlibrary libbackend.a \ | ||||
|     lib/decoders/fluxmapreader.cc \ | ||||
|     lib/decoders/fmmfm.cc \ | ||||
|     lib/encoders/encoders.cc \ | ||||
|     lib/f85/decoder.cc \ | ||||
|     lib/fb100/decoder.cc \ | ||||
|     lib/flags.cc \ | ||||
|     lib/fluxmap.cc \ | ||||
|     lib/fluxsink/fluxsink.cc \ | ||||
| @@ -163,20 +168,19 @@ buildlibrary libbackend.a \ | ||||
|     lib/fluxsource/kryoflux.cc \ | ||||
|     lib/fluxsource/sqlitefluxsource.cc \ | ||||
|     lib/fluxsource/streamfluxsource.cc \ | ||||
| 	lib/imagereader/imagereader.cc \ | ||||
| 	lib/imagereader/imgimagereader.cc \ | ||||
| 	lib/imagewriter/imagewriter.cc \ | ||||
| 	lib/imagewriter/imgimagewriter.cc \ | ||||
|     lib/globals.cc \ | ||||
|     lib/hexdump.cc \ | ||||
|     lib/ibm/decoder.cc \ | ||||
|     lib/image.cc \ | ||||
|     lib/macintosh/decoder.cc \ | ||||
|     lib/mx/decoder.cc \ | ||||
|     lib/reader.cc \ | ||||
|     lib/sector.cc \ | ||||
|     lib/sectorset.cc \ | ||||
|     lib/sql.cc \ | ||||
|     lib/usb.cc \ | ||||
|     lib/victor9k/decoder.cc \ | ||||
|     lib/writer.cc \ | ||||
|     lib/zilogmcz/decoder.cc \ | ||||
|  | ||||
| buildlibrary libfrontend.a \ | ||||
|     src/fe-cwftoflux.cc \ | ||||
|   | ||||
| @@ -34,14 +34,15 @@ int mainConvertFluxToAu(int argc, const char* argv[]) | ||||
| { | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
|     const auto& locations = source.get().locations; | ||||
|     FluxSpec spec(source); | ||||
|     const auto& locations = spec.locations; | ||||
|     if (locations.size() != 1) | ||||
|         Error() << "the source dataspec must contain exactly one track (two sides count as two tracks)"; | ||||
|     const auto& location = *(locations.begin()); | ||||
|  | ||||
|     std::cerr << "Reading source flux...\n"; | ||||
|     setHardwareFluxSourceDensity(highDensityFlag); | ||||
|     std::shared_ptr<FluxSource> fluxsource = FluxSource::create(source); | ||||
|     std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec); | ||||
|     const auto& fluxmap = fluxsource->readFlux(location.track, location.side); | ||||
|     unsigned totalTicks = fluxmap->ticks() + 2; | ||||
|     unsigned channels = withIndex ? 2 : 1; | ||||
|   | ||||
| @@ -30,14 +30,15 @@ int mainConvertFluxToVcd(int argc, const char* argv[]) | ||||
| { | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
|     const auto& locations = source.get().locations; | ||||
|     const FluxSpec spec(source); | ||||
|     const auto& locations = spec.locations; | ||||
|     if (locations.size() != 1) | ||||
|         Error() << "the source dataspec must contain exactly one track (two sides count as two tracks)"; | ||||
|     const auto& location = *(locations.begin()); | ||||
|  | ||||
|     std::cerr << "Reading source flux...\n"; | ||||
|     setHardwareFluxSourceDensity(highDensityFlag); | ||||
|     std::shared_ptr<FluxSource> fluxsource = FluxSource::create(source); | ||||
|     std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec); | ||||
|     const auto& fluxmap = fluxsource->readFlux(location.track, location.side); | ||||
|  | ||||
|     std::cerr << "Writing destination VCD...\n"; | ||||
|   | ||||
| @@ -12,11 +12,6 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "adfs.img"); | ||||
|  | ||||
| static IntFlag sectorIdBase( | ||||
| 	{ "--sector-id-base" }, | ||||
| 	"Sector ID of the first sector.", | ||||
| @@ -25,10 +20,11 @@ static IntFlag sectorIdBase( | ||||
| int mainReadADFS(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0-1"); | ||||
| 	setReaderDefaultOutput("adfs.img"); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	IbmDecoder decoder(sectorIdBase); | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -20,11 +20,12 @@ static StringFlag outputFilename( | ||||
| int mainReadAESLanier(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0"); | ||||
| 	setReaderDefaultOutput("aeslanier.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	AesLanierDecoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -13,19 +13,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "amiga.adf"); | ||||
|  | ||||
| int mainReadAmiga(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0-1"); | ||||
| 	setReaderDefaultOutput("amiga.adf:c=80:h=2:s=11:b=512"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	AmigaDecoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -12,11 +12,6 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "ampro.img"); | ||||
|  | ||||
| static IntFlag sectorIdBase( | ||||
| 	{ "--sector-id-base" }, | ||||
| 	"Sector ID of the first sector.", | ||||
| @@ -25,11 +20,12 @@ static IntFlag sectorIdBase( | ||||
| int mainReadAmpro(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0"); | ||||
| 	setReaderDefaultOutput("ampro.adf"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	IbmDecoder decoder(sectorIdBase); | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -13,19 +13,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "apple2.img"); | ||||
|  | ||||
| int mainReadApple2(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0"); | ||||
| 	setReaderDefaultOutput("apple2.adf"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	Apple2Decoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -14,19 +14,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "brother.img"); | ||||
|  | ||||
| int mainReadBrother(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-81:s=0"); | ||||
| 	setReaderDefaultOutput("brother.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	BrotherDecoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -13,19 +13,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "c64.img"); | ||||
|  | ||||
| int mainReadC64(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79x2:s=0"); | ||||
| 	setReaderDefaultOutput("c64.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	Commodore64Decoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -12,11 +12,6 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "dfs.img"); | ||||
|  | ||||
| static IntFlag sectorIdBase( | ||||
| 	{ "--sector-id-base" }, | ||||
| 	"Sector ID of the first sector.", | ||||
| @@ -25,11 +20,12 @@ static IntFlag sectorIdBase( | ||||
| int mainReadDFS(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0"); | ||||
| 	setReaderDefaultOutput("dfs.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	IbmDecoder decoder(sectorIdBase); | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -13,19 +13,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "f85.img"); | ||||
|  | ||||
| int mainReadF85(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0"); | ||||
| 	setReaderDefaultOutput("f85.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	DurangoF85Decoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -12,19 +12,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "fb100.img"); | ||||
|  | ||||
| int mainReadFB100(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79x2:s=0"); | ||||
| 	setReaderDefaultOutput("fb100.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	Fb100Decoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -12,11 +12,6 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "ibm.img"); | ||||
|  | ||||
| static IntFlag sectorIdBase( | ||||
| 	{ "--sector-id-base" }, | ||||
| 	"Sector ID of the first sector.", | ||||
| @@ -25,10 +20,11 @@ static IntFlag sectorIdBase( | ||||
| int mainReadIBM(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0-1"); | ||||
| 	setReaderDefaultOutput("ibm.img"); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	IbmDecoder decoder(sectorIdBase); | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -13,19 +13,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "mac.img"); | ||||
|  | ||||
| int mainReadMac(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0-1"); | ||||
| 	setReaderDefaultOutput("mac.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	MacintoshDecoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -12,18 +12,14 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "mx.img"); | ||||
|  | ||||
| int mainReadMx(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0-1"); | ||||
| 	setReaderDefaultOutput("mx.img"); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	MxDecoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -13,19 +13,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "victor9k.img"); | ||||
|  | ||||
| int mainReadVictor9K(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0"); | ||||
| 	setReaderDefaultOutput("victor9k.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	Victor9kDecoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -12,19 +12,15 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "zilogmcz.img"); | ||||
|  | ||||
| int mainReadZilogMCZ(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-76:s=0"); | ||||
| 	setReaderDefaultOutput("zilogmcz.img"); | ||||
|     setReaderRevolutions(2); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	ZilogMczDecoder decoder; | ||||
| 	readDiskCommand(decoder, outputFilename); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,8 @@ int mainRpm(int argc, const char* argv[]) | ||||
| { | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
|     usbSetDrive(source.get().drive, false); | ||||
|     FluxSpec spec(source); | ||||
|     usbSetDrive(spec.drive, false); | ||||
|     nanoseconds_t period = usbGetRotationalPeriod(); | ||||
|     std::cout << "Rotational period is " << period/1000 << " ms (" << 60e6/period << " rpm)" << std::endl; | ||||
|  | ||||
|   | ||||
| @@ -10,19 +10,14 @@ | ||||
|  | ||||
| static FlagGroup flags { &writerFlags, &brotherEncoderFlags }; | ||||
|  | ||||
| static StringFlag inputFilename( | ||||
|     { "--input", "-i" }, | ||||
|     "The input image file to read from.", | ||||
|     "brother.img"); | ||||
|  | ||||
| int mainWriteBrother(int argc, const char* argv[]) | ||||
| { | ||||
|     setWriterDefaultInput(":c=78:h=1:s=12:b=256"); | ||||
| 	setWriterDefaultDest(":d=0:t=0-77:s=0"); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	BrotherEncoder encoder; | ||||
| 	Geometry geometry = {78, 1, 12, 256}; | ||||
| 	writeDiskCommand(encoder, geometry, inputFilename); | ||||
| 	writeDiskCommand(encoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -33,48 +33,79 @@ static void test_parsemod(void) | ||||
|         == (DataSpec::Modifier{"x", {2, 4, 9}})); | ||||
| } | ||||
|  | ||||
| static void test_dataspec(void) | ||||
| static void test_fluxspec(void) | ||||
| { | ||||
|     DataSpec spec("foo:t=0-2:s=0-1:d=0"); | ||||
|     assert(spec.filename == "foo"); | ||||
|     assert((spec.locations | ||||
|         == std::vector<DataSpec::Location> | ||||
|         {{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}})); | ||||
|     assert((std::string)spec == "foo:d=0:s=0-1:t=0-2"); | ||||
|  | ||||
|     { | ||||
|         FluxSpec fspec(spec); | ||||
|         assert(fspec.filename == "foo"); | ||||
|         assert((fspec.locations | ||||
|             == std::vector<FluxSpec::Location> | ||||
|             {{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}})); | ||||
|         assert((std::string)spec == "foo:d=0:s=0-1:t=0-2"); | ||||
|     } | ||||
|  | ||||
|     spec.set("bar"); | ||||
|     assert(spec.filename == "bar"); | ||||
|     assert((spec.locations | ||||
|         == std::vector<DataSpec::Location> | ||||
|         {{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}})); | ||||
|     assert((std::string)spec == "bar:d=0:s=0-1:t=0-2"); | ||||
|     { | ||||
|         FluxSpec fspec(spec); | ||||
|         assert(fspec.filename == "bar"); | ||||
|         assert((fspec.locations | ||||
|             == std::vector<FluxSpec::Location> | ||||
|             {{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}})); | ||||
|         assert((std::string)spec == "bar:d=0:s=0-1:t=0-2"); | ||||
|     } | ||||
|  | ||||
|     spec.set(":t=0"); | ||||
|     assert(spec.filename.empty()); | ||||
|     assert((spec.locations | ||||
|         == std::vector<DataSpec::Location> | ||||
|         {{0, 0, 0}, {0, 0, 1}})); | ||||
|     assert((std::string)spec == ":d=0:s=0-1:t=0"); | ||||
|     { | ||||
|         FluxSpec fspec(spec); | ||||
|         assert(fspec.filename.empty()); | ||||
|         assert((fspec.locations | ||||
|             == std::vector<FluxSpec::Location> | ||||
|             {{0, 0, 0}, {0, 0, 1}})); | ||||
|         assert((std::string)spec == ":d=0:s=0-1:t=0"); | ||||
|     } | ||||
|  | ||||
|     spec.set(":s=1"); | ||||
|     assert(spec.filename.empty()); | ||||
|     assert((spec.locations | ||||
|         == std::vector<DataSpec::Location> | ||||
|         {{0, 0, 1}})); | ||||
|     assert((std::string)spec == ":d=0:s=1:t=0"); | ||||
|     { | ||||
|         FluxSpec fspec(spec); | ||||
|         assert(fspec.filename.empty()); | ||||
|         assert((fspec.locations | ||||
|             == std::vector<FluxSpec::Location> | ||||
|             {{0, 0, 1}})); | ||||
|         assert((std::string)spec == ":d=0:s=1:t=0"); | ||||
|     } | ||||
|  | ||||
|     spec.set(":t=9:d=1"); | ||||
|     assert(spec.filename.empty()); | ||||
|     assert((spec.locations | ||||
|         == std::vector<DataSpec::Location> | ||||
|         {{1, 9, 1}})); | ||||
|     assert((std::string)spec == ":d=1:s=1:t=9"); | ||||
|     { | ||||
|         FluxSpec fspec(spec); | ||||
|         assert(fspec.filename.empty()); | ||||
|         assert((fspec.locations | ||||
|             == std::vector<FluxSpec::Location> | ||||
|             {{1, 9, 1}})); | ||||
|         assert((std::string)spec == ":d=1:s=1:t=9"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void test_imagespec(void) | ||||
| { | ||||
|     DataSpec spec("foo:c=9:h=2:s=99:b=256"); | ||||
|  | ||||
|     { | ||||
|         ImageSpec ispec(spec); | ||||
|         assert(ispec.filename == "foo"); | ||||
|         assert(ispec.cylinders == 9); | ||||
|         assert(ispec.heads == 2); | ||||
|         assert(ispec.sectors == 99); | ||||
|         assert(ispec.bytes = 256); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main(int argc, const char* argv[]) | ||||
| { | ||||
|     test_split(); | ||||
|     test_parsemod(); | ||||
|     test_dataspec(); | ||||
|     test_fluxspec(); | ||||
|     test_imagespec(); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user