| 65 | }; |
| 66 | |
| 67 | class A2rFluxSource : public FluxSource |
| 68 | { |
| 69 | public: |
| 70 | A2rFluxSource(const A2rFluxSourceProto& config): _config(config) |
| 71 | { |
| 72 | _data = Bytes::readFromFile(_config.filename()); |
| 73 | ByteReader br(_data); |
| 74 | |
| 75 | switch (br.read_be32()) |
| 76 | { |
| 77 | case 0x41325232: |
| 78 | { |
| 79 | _version = 2; |
| 80 | Bytes info = findChunk("INFO"); |
| 81 | int disktype = info[33]; |
| 82 | if (disktype == 1) |
| 83 | { |
| 84 | /* 5.25" with quarter stepping. */ |
| 85 | _extraConfig.mutable_drive()->set_drive_type( |
| 86 | DRIVETYPE_APPLE2); |
| 87 | } |
| 88 | else |
| 89 | { |
| 90 | /* 3.5". */ |
| 91 | _extraConfig.mutable_drive()->set_drive_type( |
| 92 | DRIVETYPE_80TRACK); |
| 93 | } |
| 94 | |
| 95 | Bytes stream = findChunk("STRM"); |
| 96 | ByteReader bsr(stream); |
| 97 | for (;;) |
| 98 | { |
| 99 | unsigned location = bsr.read_8(); |
| 100 | if (location == 0xff) |
| 101 | break; |
| 102 | auto key = (disktype == 1) |
| 103 | ? CylinderHead{location, 0} |
| 104 | : CylinderHead{location >> 1, location & 1}; |
| 105 | |
| 106 | bsr.skip(1); |
| 107 | uint32_t len = bsr.read_le32(); |
| 108 | nanoseconds_t index = (nanoseconds_t)bsr.read_le32() * 125; |
| 109 | auto it = _v2data.find(key); |
| 110 | if (it == _v2data.end()) |
| 111 | { |
| 112 | _v2data[key] = std::make_unique<A2Rv2Flux>(); |
| 113 | it = _v2data.find(key); |
| 114 | it->second->index = index; |
| 115 | } |
| 116 | |
| 117 | it->second->flux.push_back(bsr.read(len)); |
| 118 | } |
| 119 | |
| 120 | auto keys = std::views::keys(_v2data); |
| 121 | std::vector<CylinderHead> chs{keys.begin(), keys.end()}; |
| 122 | unsigned minCylinder = std::ranges::min( |
| 123 | chs | std::views::transform(&CylinderHead::cylinder)); |
| 124 | unsigned maxCylinder = std::ranges::min( |
nothing calls this directly
no outgoing calls
no test coverage detected