diff --git a/Storage/Disk/Parsers/FAT.cpp b/Storage/Disk/Parsers/FAT.cpp index 8e403c77d..2cdd71756 100644 --- a/Storage/Disk/Parsers/FAT.cpp +++ b/Storage/Disk/Parsers/FAT.cpp @@ -15,18 +15,19 @@ using namespace Storage::Disk; FAT::Volume::CHS FAT::Volume::chs_for_sector(int sector) const { - const auto sectors_per_head = total_sectors / head_count; + const auto track = sector / sectors_per_track; - // Guess here: there's no head interleaving. Let's see. + // Sides are interleaved. return CHS{ - sector / sectors_per_head, - (sector % sectors_per_head) / sectors_per_track, + track / head_count, + track % head_count, 1 + (sector % sectors_per_track) }; } -int FAT::Volume::sector_for_cluster(int cluster) const { - return (cluster * sectors_per_cluster) + first_data_sector; +int FAT::Volume::sector_for_cluster(uint16_t cluster) const { + // The first cluster in the data area is numbered as 2. + return ((cluster - 2) * sectors_per_cluster) + first_data_sector; } namespace { @@ -34,6 +35,7 @@ namespace { FAT::Directory directory_from(const std::vector &contents) { FAT::Directory result; + // Worst case: parse until the amount of data supplied is fully consumed. for(size_t base = 0; base < contents.size(); base += 32) { // An entry starting with byte 0 indicates end-of-directory. if(!contents[base]) { @@ -80,6 +82,7 @@ std::optional FAT::GetVolume(const std::shared_ptr FAT::GetVolume(const std::shared_ptr> 12); + volume.fat.push_back(uint16_t(double_cluster & 0xfff)); + volume.fat.push_back(uint16_t(double_cluster >> 12)); } // Grab the root directory. std::vector root_directory; for(size_t c = 0; c < root_directory_sectors; c++) { - const auto sector_number = int(1 + c + volume.sectors_per_fat*volume.fat_copies); + const auto sector_number = int(volume.reserved_sectors + c + volume.sectors_per_fat*volume.fat_copies); const auto address = volume.chs_for_sector(sector_number); Storage::Encodings::MFM::Sector *const sector = @@ -128,19 +131,6 @@ std::optional FAT::GetVolume(const std::shared_ptr> FAT::GetFile(const std::shared_ptr contents; + + // In FAT cluster numbers describe a linked list via the FAT table, with values above $FF0 being reserved + // (relevantly: FF7 means bad cluster; FF8–FFF mean end-of-file). uint16_t cluster = file.starting_cluster; - while(contents.size() < file.size) { - int sector = volume.sector_for_cluster(cluster); - ++cluster; + do { + const int sector = volume.sector_for_cluster(cluster); for(int c = 0; c < volume.sectors_per_cluster; c++) { - const auto address = volume.chs_for_sector(sector); - ++sector; + const auto address = volume.chs_for_sector(sector + c); Storage::Encodings::MFM::Sector *const sector_contents = parser.get_sector(address.head, address.cylinder, uint8_t(address.sector)); @@ -164,7 +155,9 @@ std::optional> FAT::GetFile(const std::shared_ptrsamples[0].begin(), sector_contents->samples[0].end(), std::back_inserter(contents)); } - } + + cluster = volume.fat[cluster]; + } while(cluster < 0xff0); return contents; } diff --git a/Storage/Disk/Parsers/FAT.hpp b/Storage/Disk/Parsers/FAT.hpp index cbf6cdb85..3ede18518 100644 --- a/Storage/Disk/Parsers/FAT.hpp +++ b/Storage/Disk/Parsers/FAT.hpp @@ -44,7 +44,7 @@ using Directory = std::vector; struct Volume { uint16_t bytes_per_sector = 0; uint8_t sectors_per_cluster = 0; - uint8_t reserved_sectors = 0; + uint16_t reserved_sectors = 0; uint8_t fat_copies = 0; uint16_t total_sectors = 0; uint16_t sectors_per_fat = 0; @@ -54,7 +54,7 @@ struct Volume { bool correct_signature = false; int first_data_sector = 0; - std::vector fat; + std::vector fat; Directory root_directory; struct CHS { @@ -65,7 +65,7 @@ struct Volume { /// @returns a direct sector -> CHS address translation. CHS chs_for_sector(int sector) const; /// @returns the CHS address for the numbered cluster within the data area. - int sector_for_cluster(int cluster) const; + int sector_for_cluster(uint16_t cluster) const; }; std::optional GetVolume(const std::shared_ptr &disk);