// // CPM.cpp // Clock Signal // // Created by Thomas Harte on 10/08/2017. // Copyright © 2017 Thomas Harte. All rights reserved. // #include "CPM.hpp" #include "../Encodings/MFM.hpp" using namespace Storage::Disk::CPM; std::unique_ptr Storage::Disk::CPM::GetCatalogue(const std::shared_ptr &disk, const ParameterBlock ¶meters) { Storage::Encodings::MFM::Parser parser(true, disk); // Assemble the actual bytes of the catalogue. std::vector catalogue; size_t sector_size = 1; uint16_t catalogue_allocation_bitmap = parameters.catalogue_allocation_bitmap; if(!catalogue_allocation_bitmap) return nullptr; int sector = 0; int track = parameters.reserved_tracks; while(catalogue_allocation_bitmap) { if(catalogue_allocation_bitmap & 0x8000) { size_t size_read = 0; do { std::shared_ptr sector_contents = parser.get_sector(0, (uint8_t)track, (uint8_t)(parameters.first_sector + sector)); if(!sector_contents) { return nullptr; } catalogue.insert(catalogue.end(), sector_contents->data.begin(), sector_contents->data.end()); sector_size = sector_contents->data.size(); size_read += sector_size; sector++; if(sector == parameters.sectors_per_track) { sector = 0; track++; } } while(size_read < (size_t)parameters.block_size); } catalogue_allocation_bitmap <<= 1; } std::unique_ptr result(new Catalogue); bool has_long_allocation_units = (parameters.tracks * parameters.sectors_per_track * (int)sector_size / parameters.block_size) >= 256; size_t bytes_per_catalogue_entry = (has_long_allocation_units ? 16 : 8) * (size_t)parameters.block_size; // From the catalogue, create files. std::map, size_t> indices_by_name; File empty_file; for(size_t c = 0; c < catalogue.size(); c += 32) { // Skip this file if it's deleted; this is marked by it having 0xe5 as its user number if(catalogue[c] == 0xe5) continue; // Check whether this file has yet been seen; if not then add it to the list std::vector descriptor; size_t index; descriptor.insert(descriptor.begin(), &catalogue[c], &catalogue[c + 12]); auto iterator = indices_by_name.find(descriptor); if(iterator != indices_by_name.end()) { index = iterator->second; } else { File new_file; new_file.user_number = catalogue[c]; for(size_t s = 0; s < 8; s++) new_file.name.push_back((char)catalogue[c + s + 1]); for(size_t s = 0; s < 3; s++) new_file.type.push_back((char)catalogue[c + s + 9] & 0x7f); new_file.read_only = catalogue[c + 9] & 0x80; new_file.system = catalogue[c + 10] & 0x80; index = result->files.size(); result->files.push_back(new_file); indices_by_name[descriptor] = index; } // figure out where this data needs to be pasted in size_t extent = (size_t)(catalogue[c + 12] + (catalogue[c + 14] << 5)); int number_of_records = catalogue[c + 15]; size_t required_size = extent * bytes_per_catalogue_entry + (size_t)number_of_records * 128; if(result->files[index].data.size() < required_size) { result->files[index].data.resize(required_size); } int sectors_per_block = parameters.block_size / (int)sector_size; int records_per_sector = (int)sector_size / 128; int record = 0; for(size_t block = 0; block < (has_long_allocation_units ? 8 : 16) && record < number_of_records; block++) { int block_number; if(has_long_allocation_units) { block_number = catalogue[c + 16 + (block << 1)] + (catalogue[c + 16 + (block << 1) + 1] << 8); } else { block_number = catalogue[c + 16 + block]; } if(!block_number) { record += parameters.block_size / 128; continue; } int first_sector = block_number * sectors_per_block; sector = first_sector % parameters.sectors_per_track; track = first_sector / parameters.sectors_per_track; for(int s = 0; s < sectors_per_block && record < number_of_records; s++) { std::shared_ptr sector_contents = parser.get_sector(0, (uint8_t)track, (uint8_t)(parameters.first_sector + sector)); if(!sector_contents) break; sector++; if(sector == parameters.sectors_per_track) { sector = 0; track++; } int records_to_copy = std::min(number_of_records - record, records_per_sector); memcpy(&result->files[index].data[extent * bytes_per_catalogue_entry + (size_t)record * 128], sector_contents->data.data(), (size_t)records_to_copy * 128); record += records_to_copy; } } } return result; }