2021-12-04 13:22:02 +00:00
|
|
|
/*
|
|
|
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
2022-12-11 22:00:52 +00:00
|
|
|
Copyright (C) 2018-22 divingkatae and maximum
|
2021-12-04 13:22:02 +00:00
|
|
|
(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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @file Support for reading and writing of various floppy images. */
|
|
|
|
|
2022-09-03 22:20:29 +00:00
|
|
|
#include <devices/floppy/floppyimg.h>
|
|
|
|
#include <machines/machineproperties.h>
|
2021-12-04 13:22:02 +00:00
|
|
|
#include <loguru.hpp>
|
|
|
|
#include <memaccess.h>
|
|
|
|
|
|
|
|
#include <cinttypes>
|
2022-02-16 23:50:37 +00:00
|
|
|
#include <cstring>
|
2022-02-06 14:23:30 +00:00
|
|
|
#include <fstream>
|
2021-12-04 13:22:02 +00:00
|
|
|
#include <string>
|
|
|
|
|
2022-02-06 14:23:30 +00:00
|
|
|
static FlopImgType identify_image(std::ifstream& img_file)
|
2021-12-04 13:22:02 +00:00
|
|
|
{
|
|
|
|
// 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 };
|
|
|
|
|
2022-02-06 14:23:30 +00:00
|
|
|
img_file.seekg(0, std::ios::beg);
|
|
|
|
img_file.read((char *)buf, sizeof(buf));
|
2021-12-04 13:22:02 +00:00
|
|
|
|
|
|
|
// 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
|
2022-02-06 14:23:30 +00:00
|
|
|
img_file.seekg(2*BLOCK_SIZE + offset, std::ios::beg);
|
|
|
|
img_file.read((char *)buf, sizeof(buf));
|
2021-12-04 13:22:02 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-02 23:24:06 +00:00
|
|
|
return FlopImgType::UNKNOWN;
|
2021-12-04 13:22:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//======================= RAW IMAGE CONVERTER ============================
|
2022-02-06 14:23:30 +00:00
|
|
|
RawFloppyImg::RawFloppyImg(std::string& file_path) : FloppyImgConverter()
|
2021-12-04 13:22:02 +00:00
|
|
|
{
|
|
|
|
this->img_path = file_path;
|
|
|
|
}
|
|
|
|
|
2022-09-03 22:20:29 +00:00
|
|
|
/**
|
|
|
|
For raw images, we'll attempt to guess disk format based on image size.
|
2021-12-04 13:22:02 +00:00
|
|
|
*/
|
2022-02-06 20:23:20 +00:00
|
|
|
int RawFloppyImg::calc_phys_params()
|
2021-12-04 13:22:02 +00:00
|
|
|
{
|
2022-02-06 14:23:30 +00:00
|
|
|
std::ifstream img_file;
|
|
|
|
|
|
|
|
img_file.open(img_path, std::ios::in | std::ios::binary);
|
|
|
|
if (img_file.fail()) {
|
|
|
|
img_file.close();
|
2021-12-04 13:22:02 +00:00
|
|
|
LOG_F(ERROR, "RawFloppyImg: Could not open specified floppy image!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine image size
|
2022-02-06 14:23:30 +00:00
|
|
|
img_file.seekg(0, img_file.end);
|
2022-12-21 11:20:39 +00:00
|
|
|
size_t img_size = img_file.tellg();
|
|
|
|
if (img_size > 2*1024*1024) {
|
|
|
|
LOG_F(ERROR, "RawFloppyImg: image size is too large to determine disk format from image size!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
this->img_size = (int)img_size;
|
2022-02-06 14:23:30 +00:00
|
|
|
img_file.seekg(0, img_file.beg);
|
|
|
|
|
2022-12-11 22:00:52 +00:00
|
|
|
img_file.close();
|
|
|
|
|
2022-02-06 14:23:30 +00:00
|
|
|
// verify image size
|
2021-12-04 13:22:02 +00:00
|
|
|
if (this->img_size < 5*BLOCK_SIZE) {
|
|
|
|
LOG_F(ERROR, "RawFloppyImg: image too short!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->img_size > MFM_HD_SIZE) {
|
|
|
|
LOG_F(ERROR, "RawFloppyImg: image too big!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-09-03 22:20:29 +00:00
|
|
|
// raw images don't include anything other than raw disk data
|
|
|
|
this->data_size = this->img_size;
|
2021-12-04 13:22:02 +00:00
|
|
|
|
2022-09-03 22:20:29 +00:00
|
|
|
// see if user has specified disk format manually
|
|
|
|
std::string fmt = GET_STR_PROP("fdd_fmt");
|
|
|
|
if (!fmt.empty()) {
|
|
|
|
if (fmt == "GCR_400K") {
|
|
|
|
this->img_size = 409600;
|
|
|
|
} else if (fmt == "GCR_800K") {
|
|
|
|
this->img_size = 819200;
|
|
|
|
} else if (fmt == "MFM_720K") {
|
|
|
|
this->img_size = 737280;
|
|
|
|
} else if (fmt == "MFM_1440K") {
|
|
|
|
this->img_size = 1474560;
|
|
|
|
} else {
|
|
|
|
LOG_F(WARNING, "Invalid floppy disk format %s", fmt.c_str());
|
|
|
|
}
|
2021-12-04 13:22:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-06 20:23:20 +00:00
|
|
|
// 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;
|
2022-02-13 02:05:55 +00:00
|
|
|
|
|
|
|
// 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;
|
2022-02-06 20:23:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->rec_method == -1) {
|
|
|
|
LOG_F(ERROR, "RawFloppyImg: could't determine disk format from image size!");
|
|
|
|
return -1;
|
|
|
|
}
|
2021-12-04 13:22:02 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-06 20:23:20 +00:00
|
|
|
/** Retrieve raw disk data. */
|
|
|
|
int RawFloppyImg::get_raw_disk_data(char* buf)
|
2021-12-04 13:22:02 +00:00
|
|
|
{
|
2022-02-06 20:23:20 +00:00
|
|
|
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();
|
|
|
|
|
2021-12-04 13:22:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Convert low-level disk data to high-level image data. */
|
|
|
|
int RawFloppyImg::export_data()
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-12-11 22:00:52 +00:00
|
|
|
// ====================== DISK COPY 4.2 IMAGE CONVERTER ======================
|
|
|
|
DiskCopy42Img::DiskCopy42Img(std::string& file_path) : FloppyImgConverter()
|
|
|
|
{
|
|
|
|
this->img_path = file_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
int DiskCopy42Img::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, "DiskCopy42Img: could not open specified floppy image!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine image size
|
|
|
|
img_file.seekg(0, img_file.end);
|
2022-12-21 11:20:39 +00:00
|
|
|
size_t img_size = img_file.tellg();
|
2022-12-11 22:00:52 +00:00
|
|
|
img_file.seekg(0, img_file.beg);
|
|
|
|
|
|
|
|
// get data size from image
|
|
|
|
uint8_t buf[4];
|
|
|
|
img_file.seekg(0x40, img_file.beg);
|
|
|
|
img_file.read((char *)&buf, 4);
|
|
|
|
this->data_size = READ_DWORD_BE_U(buf);
|
|
|
|
|
2022-12-21 11:20:39 +00:00
|
|
|
if (this->data_size > img_size) {
|
2022-12-11 22:00:52 +00:00
|
|
|
img_file.close();
|
|
|
|
LOG_F(ERROR, "DiskCopy42Img: invalid data size %d", this->data_size);
|
|
|
|
return -1;
|
|
|
|
}
|
2022-12-21 11:20:39 +00:00
|
|
|
this->img_size = (int)img_size;
|
2022-12-11 22:00:52 +00:00
|
|
|
|
|
|
|
uint8_t disk_format = 0xFFU;
|
|
|
|
|
|
|
|
img_file.seekg(0x50, img_file.beg);
|
|
|
|
img_file.read((char *)&disk_format, 1);
|
|
|
|
img_file.read((char *)&this->format_byte, 1);
|
|
|
|
|
|
|
|
img_file.close();
|
|
|
|
|
|
|
|
this->density = 0; // assume double density by default
|
|
|
|
|
|
|
|
this->num_tracks = 80;
|
2022-12-12 01:27:42 +00:00
|
|
|
this->num_sides = ((this->format_byte >> 5) & 1) + 1;
|
2022-12-11 22:00:52 +00:00
|
|
|
|
|
|
|
switch (disk_format) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
this->rec_method = 0; // GCR
|
|
|
|
this->num_sectors = 800;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
this->rec_method = 1; // MFM
|
|
|
|
this->num_sectors = 1440;
|
2022-12-12 01:27:42 +00:00
|
|
|
this->format_byte = 2;
|
2022-12-11 22:00:52 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
this->rec_method = 1; // MFM
|
|
|
|
this->density = 1; // report high density
|
|
|
|
this->num_sectors = 2880;
|
2022-12-12 01:27:42 +00:00
|
|
|
this->format_byte = 2;
|
2022-12-11 22:00:52 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_F(ERROR, "DiskCopy42Img: invalid disk format %X", disk_format);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int DiskCopy42Img::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, "DiskCopy42Img: could not open specified floppy image!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
img_file.seekg(0x54, img_file.beg);
|
|
|
|
img_file.read(buf, this->data_size);
|
|
|
|
img_file.close();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int DiskCopy42Img::export_data(void) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-06 20:23:20 +00:00
|
|
|
FloppyImgConverter* open_floppy_image(std::string& img_path)
|
2021-12-04 13:22:02 +00:00
|
|
|
{
|
2022-12-11 22:00:52 +00:00
|
|
|
FloppyImgConverter *fconv = nullptr;
|
2021-12-04 13:22:02 +00:00
|
|
|
|
2022-02-06 14:23:30 +00:00
|
|
|
std::ifstream img_file;
|
|
|
|
|
|
|
|
img_file.open(img_path, std::ios::in | std::ios::binary);
|
|
|
|
if (img_file.fail()) {
|
|
|
|
img_file.close();
|
2021-12-04 13:22:02 +00:00
|
|
|
LOG_F(ERROR, "Could not open specified floppy image!");
|
2022-02-06 20:23:20 +00:00
|
|
|
return nullptr;
|
2021-12-04 13:22:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FlopImgType itype = identify_image(img_file);
|
|
|
|
|
2022-02-06 14:23:30 +00:00
|
|
|
img_file.close();
|
2021-12-04 13:22:02 +00:00
|
|
|
|
|
|
|
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");
|
2022-12-11 22:00:52 +00:00
|
|
|
fconv = new DiskCopy42Img(img_path);
|
2021-12-04 13:22:02 +00:00
|
|
|
break;
|
|
|
|
case FlopImgType::WOZ1:
|
|
|
|
case FlopImgType::WOZ2:
|
2022-08-14 12:26:56 +00:00
|
|
|
LOG_F(INFO, "WOZ v%s image", (itype == FlopImgType::WOZ2) ? "2" : "1");
|
2021-12-04 13:22:02 +00:00
|
|
|
break;
|
|
|
|
default:
|
2022-10-20 11:33:17 +00:00
|
|
|
LOG_F(WARNING, "Unknown image format - assume RAW");
|
|
|
|
fconv = new RawFloppyImg(img_path);
|
2021-12-04 13:22:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-06 20:23:20 +00:00
|
|
|
if (fconv->calc_phys_params()) {
|
|
|
|
delete fconv;
|
|
|
|
return nullptr;
|
2021-12-04 13:22:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-06 20:23:20 +00:00
|
|
|
return fconv;
|
2021-12-04 13:22:02 +00:00
|
|
|
}
|