1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-09 21:29:53 +00:00

Adds an intermediate step in CP/M directory parsing.

To reduce amount of time spent allocating and reallocating buffers.
This commit is contained in:
Thomas Harte 2017-09-29 21:38:16 -04:00
parent 698ffca51b
commit 19eb975c73

View File

@ -46,77 +46,109 @@ std::unique_ptr<Storage::Disk::CPM::Catalogue> Storage::Disk::CPM::GetCatalogue(
catalogue_allocation_bitmap <<= 1; catalogue_allocation_bitmap <<= 1;
} }
std::unique_ptr<Catalogue> result(new Catalogue); struct CatalogueEntry {
bool has_long_allocation_units = (parameters.tracks * parameters.sectors_per_track * (int)sector_size / parameters.block_size) >= 256; uint8_t user_number;
size_t bytes_per_catalogue_entry = (has_long_allocation_units ? 16 : 8) * (size_t)parameters.block_size; std::string name;
std::string type;
bool read_only;
bool system;
size_t extent;
uint8_t number_of_records;
size_t catalogue_index;
// From the catalogue, create files. bool operator < (const CatalogueEntry &rhs) const {
std::map<std::vector<uint8_t>, size_t> indices_by_name; return std::tie(user_number, name, type, extent) < std::tie(rhs.user_number, rhs.name, rhs.type, rhs.extent);
File empty_file; }
bool is_same_file(const CatalogueEntry &rhs) const {
return std::tie(user_number, name, type) == std::tie(rhs.user_number, rhs.name, rhs.type);
}
};
// From the catalogue, get catalogue entries.
std::vector<CatalogueEntry> catalogue_entries;
for(size_t c = 0; c < catalogue.size(); c += 32) { 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 // Skip this file if it's deleted; this is marked by it having 0xe5 as its user number
if(catalogue[c] == 0xe5) continue; if(catalogue[c] == 0xe5) continue;
// Check whether this file has yet been seen; if not then add it to the list catalogue_entries.emplace_back();
std::vector<uint8_t> descriptor; CatalogueEntry &entry = catalogue_entries.back();
size_t index; entry.user_number = catalogue[c];
descriptor.insert(descriptor.begin(), &catalogue[c], &catalogue[c + 12]); entry.name.insert(entry.name.begin(), &catalogue[c+1], &catalogue[c+9]);
auto iterator = indices_by_name.find(descriptor); for(size_t s = 0; s < 3; s++) entry.type.push_back((char)catalogue[c + s + 9] & 0x7f);
if(iterator != indices_by_name.end()) { entry.read_only = catalogue[c + 9] & 0x80;
index = iterator->second; entry.system = catalogue[c + 10] & 0x80;
} else { entry.extent = (size_t)(catalogue[c + 12] + (catalogue[c + 14] << 5));
File new_file; entry.number_of_records = catalogue[c + 15];
new_file.user_number = catalogue[c]; entry.catalogue_index = 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; // Sort the catalogue entries and then map to files.
new_file.system = catalogue[c + 10] & 0x80; std::sort(catalogue_entries.begin(), catalogue_entries.end());
index = result->files.size();
result->files.push_back(new_file); std::unique_ptr<Catalogue> result(new Catalogue);
indices_by_name[descriptor] = index;
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;
int sectors_per_block = parameters.block_size / (int)sector_size;
int records_per_sector = (int)sector_size / 128;
auto entry = catalogue_entries.begin();
while(entry != catalogue_entries.end()) {
// Find final catalogue entry that relates to the same file.
auto final_entry = entry + 1;
while(final_entry != catalogue_entries.end() && final_entry->is_same_file(*entry)) {
final_entry++;
} }
final_entry--;
// figure out where this data needs to be pasted in // Create file.
size_t extent = (size_t)(catalogue[c + 12] + (catalogue[c + 14] << 5)); result->files.emplace_back();
int number_of_records = catalogue[c + 15]; File &new_file = result->files.back();
new_file.user_number = entry->user_number;
new_file.name = std::move(entry->name);
new_file.type = std::move(entry->type);
new_file.read_only = entry->read_only;
new_file.system = entry->system;
size_t required_size = extent * bytes_per_catalogue_entry + (size_t)number_of_records * 128; // Create storage for data.
if(result->files[index].data.size() < required_size) { size_t required_size = final_entry->extent * bytes_per_catalogue_entry + (size_t)final_entry->number_of_records * 128;
result->files[index].data.resize(required_size); new_file.data.resize(required_size);
}
int sectors_per_block = parameters.block_size / (int)sector_size; // Accumulate all data.
int records_per_sector = (int)sector_size / 128; while(entry <= final_entry) {
int record = 0; int record = 0;
for(size_t block = 0; block < (has_long_allocation_units ? 8 : 16) && record < number_of_records; block++) { for(size_t block = 0; block < (has_long_allocation_units ? 8 : 16) && record < entry->number_of_records; block++) {
int block_number; int block_number;
if(has_long_allocation_units) { if(has_long_allocation_units) {
block_number = catalogue[c + 16 + (block << 1)] + (catalogue[c + 16 + (block << 1) + 1] << 8); block_number = catalogue[entry->catalogue_index + 16 + (block << 1)] + (catalogue[entry->catalogue_index + 16 + (block << 1) + 1] << 8);
} else { } else {
block_number = catalogue[c + 16 + block]; block_number = catalogue[entry->catalogue_index + 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++) {
Storage::Encodings::MFM::Sector *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++;
} }
if(!block_number) {
record += parameters.block_size / 128;
continue;
}
int first_sector = block_number * sectors_per_block;
int records_to_copy = std::min(number_of_records - record, records_per_sector); sector = first_sector % parameters.sectors_per_track;
memcpy(&result->files[index].data[extent * bytes_per_catalogue_entry + (size_t)record * 128], sector_contents->data.data(), (size_t)records_to_copy * 128); track = first_sector / parameters.sectors_per_track;
record += records_to_copy;
for(int s = 0; s < sectors_per_block && record < entry->number_of_records; s++) {
Storage::Encodings::MFM::Sector *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(entry->number_of_records - record, records_per_sector);
memcpy(&new_file.data[entry->extent * bytes_per_catalogue_entry + (size_t)record * 128], sector_contents->data.data(), (size_t)records_to_copy * 128);
record += records_to_copy;
}
} }
entry++;
} }
} }