/* DingusPPC - The Experimental PowerPC Macintosh emulator Copyright (C) 2018-21 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** @file Support for reading and writing of various floppy images. */ #include "floppyimg.h" #include #include #include #include #include #include static FlopImgType identify_image(std::ifstream& img_file) { // WOZ images identification strings static uint8_t WOZ1_SIG[] = {0x57, 0x4F, 0x5A, 0x31, 0xFF, 0x0A, 0x0D, 0x0A}; static uint8_t WOZ2_SIG[] = {0x57, 0x4F, 0x5A, 0x32, 0xFF, 0x0A, 0x0D, 0x0A}; uint8_t buf[8] = { 0 }; img_file.seekg(0, std::ios::beg); img_file.read((char *)buf, sizeof(buf)); // WOZ files are easily identified if (!std::memcmp(buf, WOZ1_SIG, sizeof(buf))) { return FlopImgType::WOZ1; } else if (!std::memcmp(buf, WOZ2_SIG, sizeof(buf))) { return FlopImgType::WOZ2; } else { for (int offset = 0; offset <=84; offset += 84) { // rewind to logical block 2 img_file.seekg(2*BLOCK_SIZE + offset, std::ios::beg); img_file.read((char *)buf, sizeof(buf)); // check for HFS/MFS signature at the start of the logical block 2 if ((buf[0] == 0x42 && buf[1] == 0x44) || (buf[0] == 0xD2 && buf[1] == 0xD7)) { if (offset) { return FlopImgType::DC42; } else { return FlopImgType::RAW; } } } } return FlopImgType::UNKNOWN; } static int64_t get_hfs_vol_size(const uint8_t *mdb_data) { uint16_t drNmAlBlks = READ_WORD_BE_A(&mdb_data[18]); uint32_t drAlBlkSiz = READ_DWORD_BE_A(&mdb_data[20]); // calculate size of the volume bitmap uint32_t vol_bmp_size = (((drNmAlBlks + 8) >> 3) + 512) & 0xFFFFFE00UL; return (drNmAlBlks * drAlBlkSiz + vol_bmp_size + 3*BLOCK_SIZE); } //======================= RAW IMAGE CONVERTER ============================ RawFloppyImg::RawFloppyImg(std::string& file_path) : FloppyImgConverter() { this->img_path = file_path; } /** For raw images, we're going to ensure that the data fits into one of the supported floppy disk sizes as well as image size matches the size of the embedded HFS/MFS volume. Then we'll attempt to guess disk format based on image size. */ int RawFloppyImg::calc_phys_params() { std::ifstream img_file; img_file.open(img_path, std::ios::in | std::ios::binary); if (img_file.fail()) { img_file.close(); LOG_F(ERROR, "RawFloppyImg: Could not open specified floppy image!"); return -1; } // determine image size img_file.seekg(0, img_file.end); this->img_size = img_file.tellg(); img_file.seekg(0, img_file.beg); // verify image size if (this->img_size < 5*BLOCK_SIZE) { img_file.close(); LOG_F(ERROR, "RawFloppyImg: image too short!"); return -1; } if (this->img_size > MFM_HD_SIZE) { img_file.close(); LOG_F(ERROR, "RawFloppyImg: image too big!"); return -1; } // read Master Directory Block from logical block 2 uint8_t buf[512] = { 0 }; img_file.seekg(2*BLOCK_SIZE, img_file.beg); img_file.read((char *)buf, sizeof(buf)); img_file.close(); uint64_t vol_size = 0; if (buf[0] == 0x42 && buf[1] == 0x44) { // check HFS volume size vol_size = get_hfs_vol_size(buf); } else if (buf[0] == 0xD2 && buf[1] == 0xD7) { // check MFS volume size } else { LOG_F(ERROR, "RawFloppyImg: unknown volume type!"); return -1; } if (vol_size > this->img_size) { LOG_F(INFO, "RawFloppyImg: volume size > image size!"); LOG_F(INFO, "Volume size: %llu, Image size: %d", vol_size, this->img_size); return -1; } // raw images don't include anything than raw disk data this->data_size = this->img_size; // guess disk format from image file size static struct { int capacity; int rec_method; int num_tracks; int num_sectors; int num_sides; int density; } size_to_params[] = { { 409600, 0, 80, 800, 1, 0}, // 400K GCR { 819200, 0, 80, 800, 2, 0}, // 800K GCR { 737280, 1, 80, 1440, 2, 0}, // 720K MFM {1474560, 1, 80, 2880, 2, 1}, // 1440K MFM }; this->rec_method = -1; for (int i = 0; i < 4; i++) { if (this->img_size == size_to_params[i].capacity) { this->rec_method = size_to_params[i].rec_method; this->num_tracks = size_to_params[i].num_tracks; this->num_sectors = size_to_params[i].num_sectors; this->num_sides = size_to_params[i].num_sides; this->density = size_to_params[i].density; // fake format byte for GCR disks if (!this->rec_method) { this->format_byte = (this->num_sides == 2) ? 0x22 : 0x2; } else { // For MFM disks this byte indicates sector size in blocks this->format_byte = 2; } break; } } if (this->rec_method == -1) { LOG_F(ERROR, "RawFloppyImg: could't determine disk format from image size!"); return -1; } return 0; } /** Retrieve raw disk data. */ int RawFloppyImg::get_raw_disk_data(char* buf) { std::ifstream img_file; img_file.open(img_path, std::ios::in | std::ios::binary); if (img_file.fail()) { img_file.close(); LOG_F(ERROR, "RawFloppyImg: Could not open specified floppy image!"); return -1; } img_file.seekg(0, img_file.beg); img_file.read(buf, this->data_size); img_file.close(); return 0; } /** Convert low-level disk data to high-level image data. */ int RawFloppyImg::export_data() { return 0; } FloppyImgConverter* open_floppy_image(std::string& img_path) { FloppyImgConverter *fconv; std::ifstream img_file; img_file.open(img_path, std::ios::in | std::ios::binary); if (img_file.fail()) { img_file.close(); LOG_F(ERROR, "Could not open specified floppy image!"); return nullptr; } FlopImgType itype = identify_image(img_file); img_file.close(); switch(itype) { case FlopImgType::RAW: LOG_F(INFO, "Raw floppy image"); fconv = new RawFloppyImg(img_path); break; case FlopImgType::DC42: LOG_F(INFO, "Disk Copy 4.2 image"); break; case FlopImgType::WOZ1: case FlopImgType::WOZ2: LOG_F(INFO, "WOZ v%s image\n", (itype == FlopImgType::WOZ2) ? "2" : "1"); break; default: LOG_F(ERROR, "Unknown/unsupported image format!"); return nullptr; } if (fconv->calc_phys_params()) { delete fconv; return nullptr; } return fconv; }