Move disk image reading to be behind an ImgFile class

Allows different implementations for different platforms (the JS
build relies on browser APIs to stream disk images over the network).

Setting aside the JS build, this also reduces some code duplication.
This commit is contained in:
Mihai Parparita 2023-09-16 14:17:27 -07:00
parent 04956c19d5
commit d4c9db7fcf
12 changed files with 188 additions and 116 deletions

View File

@ -24,7 +24,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/ata/atahd.h>
#include <cstring>
#include <sys/stat.h>
#include <fstream>
#include <string>
#include <loguru.hpp>
@ -35,16 +34,11 @@ AtaHardDisk::AtaHardDisk() : AtaBaseDevice("ATA-HD", DEVICE_TYPE_ATA) {
}
void AtaHardDisk::insert_image(std::string filename) {
this->hdd_img.open(filename, std::fstream::out | std::fstream::in | std::fstream::binary);
struct stat stat_buf;
int rc = stat(filename.c_str(), &stat_buf);
if (!rc) {
this->img_size = stat_buf.st_size;
} else {
ABORT_F("AtaHardDisk: could not determine file size using stat()");
if (!this->hdd_img.open(filename)) {
ABORT_F("AtaHardDisk: could not open image file");
}
this->hdd_img.seekg(0, std::ios_base::beg);
this->img_size = this->hdd_img.size();
}
int AtaHardDisk::perform_command()
@ -56,7 +50,6 @@ int AtaHardDisk::perform_command()
case NOP:
break;
case RECALIBRATE:
hdd_img.seekg(0, std::ios::beg);
this->r_error = 0;
this->r_cylinder_lo = 0;
this->r_cylinder_hi = 0;
@ -68,8 +61,7 @@ int AtaHardDisk::perform_command()
uint32_t sector = (r_sect_num << 16);
sector |= ((this->r_cylinder_lo) << 8) + (this->r_cylinder_hi);
uint64_t offset = sector * ATA_HD_SEC_SIZE;
hdd_img.seekg(offset, std::ios::beg);
hdd_img.read(buffer, sec_count * ATA_HD_SEC_SIZE);
hdd_img.read(buffer, offset, sec_count * ATA_HD_SEC_SIZE);
this->r_status &= ~DRQ;
}
break;
@ -80,8 +72,7 @@ int AtaHardDisk::perform_command()
uint32_t sector = (r_sect_num << 16);
sector |= ((this->r_cylinder_lo) << 8) + (this->r_cylinder_hi);
uint64_t offset = sector * ATA_HD_SEC_SIZE;
hdd_img.seekg(offset, std::ios::beg);
hdd_img.write(buffer, sec_count * ATA_HD_SEC_SIZE);
hdd_img.write(buffer, offset, sec_count * ATA_HD_SEC_SIZE);
this->r_status &= ~DRQ;
}
break;

View File

@ -25,8 +25,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#define ATA_HARD_DISK_H
#include <devices/common/ata/atabasedevice.h>
#include <utils/imgfile.h>
#include <string>
#include <fstream>
#define ATA_HD_SEC_SIZE 512
@ -40,7 +41,7 @@ public:
int perform_command() override;
private:
std::fstream hdd_img;
ImgFile hdd_img;
uint64_t img_size;
char * buffer = new char[1 <<17];

View File

@ -30,7 +30,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cinttypes>
#include <cstring>
#include <sys/stat.h>
using namespace std;
@ -50,25 +49,20 @@ ScsiCdrom::ScsiCdrom(int my_id) : ScsiDevice(my_id)
void ScsiCdrom::insert_image(std::string filename)
{
this->cdr_img.open(filename, ios::out | ios::in | ios::binary);
struct stat stat_buf;
int rc = stat(filename.c_str(), &stat_buf);
if (!rc) {
this->img_size = stat_buf.st_size;
this->total_frames = (this->img_size + this->sector_size - 1) / this->sector_size;
// create single track descriptor
this->tracks[0] = {.trk_num = 1, .adr_ctrl = 0x14, .start_lba = 0};
this->num_tracks = 1;
// create Lead-out descriptor containing all data
this->tracks[1] = {.trk_num = LEAD_OUT_TRK_NUM, .adr_ctrl = 0x14,
.start_lba = static_cast<uint32_t>(this->total_frames)};
} else {
ABORT_F("SCSI-CDROM: could not determine file size using stat()");
if (!this->cdr_img.open(filename)) {
ABORT_F("SCSI-CDROM: could not open image file");
}
this->cdr_img.seekg(0, std::ios_base::beg);
this->img_size = this->cdr_img.size();
this->total_frames = (this->img_size + this->sector_size - 1) / this->sector_size;
// create single track descriptor
this->tracks[0] = {.trk_num = 1, .adr_ctrl = 0x14, .start_lba = 0};
this->num_tracks = 1;
// create Lead-out descriptor containing all data
this->tracks[1] = {.trk_num = LEAD_OUT_TRK_NUM, .adr_ctrl = 0x14,
.start_lba = static_cast<uint32_t>(this->total_frames)};
}
void ScsiCdrom::process_command()
@ -155,8 +149,7 @@ void ScsiCdrom::read(const uint32_t lba, const uint16_t transfer_len, const uint
transfer_size *= this->sector_size;
uint64_t device_offset = lba * this->sector_size;
this->cdr_img.seekg(device_offset, this->cdr_img.beg);
this->cdr_img.read(this->data_buf, transfer_size);
this->cdr_img.read(this->data_buf, device_offset, transfer_size);
this->bytes_out = transfer_size;
this->msg_buf[0] = ScsiMessage::COMMAND_COMPLETE;

View File

@ -25,9 +25,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#define SCSI_CDROM_H
#include <devices/common/scsi/scsi.h>
#include <utils/imgfile.h>
#include <cinttypes>
#include <fstream>
#include <memory>
#include <string>
@ -74,7 +74,7 @@ private:
AddrMsf lba_to_msf(const int lba);
private:
std::fstream cdr_img;
ImgFile cdr_img;
uint64_t img_size;
int total_frames;
int num_tracks;

View File

@ -30,7 +30,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <fstream>
#include <cstring>
#include <sys/stat.h>
#define HDD_SECTOR_SIZE 512
@ -43,17 +42,12 @@ ScsiHardDisk::ScsiHardDisk(int my_id) : ScsiDevice(my_id) {
void ScsiHardDisk::insert_image(std::string filename) {
//We don't want to store everything in memory, but
//we want to keep the hard disk available.
this->hdd_img.open(filename, ios::out | ios::in | ios::binary);
struct stat stat_buf;
int rc = stat(filename.c_str(), &stat_buf);
if (!rc) {
this->img_size = stat_buf.st_size;
this->total_blocks = (this->img_size + HDD_SECTOR_SIZE - 1) / HDD_SECTOR_SIZE;
} else {
ABORT_F("ScsiHardDisk: could not determine file size using stat()");
if (!this->hdd_img.open(filename)) {
ABORT_F("ScsiHardDisk: could not open image file");
}
this->hdd_img.seekg(0, std::ios_base::beg);
this->img_size = this->hdd_img.size();
this->total_blocks = (this->img_size + HDD_SECTOR_SIZE - 1) / HDD_SECTOR_SIZE;
}
void ScsiHardDisk::process_command() {
@ -267,8 +261,7 @@ void ScsiHardDisk::read(uint32_t lba, uint16_t transfer_len, uint8_t cmd_len) {
transfer_size *= HDD_SECTOR_SIZE;
uint64_t device_offset = lba * HDD_SECTOR_SIZE;
this->hdd_img.seekg(device_offset, this->hdd_img.beg);
this->hdd_img.read(img_buffer, transfer_size);
this->hdd_img.read(img_buffer, device_offset, transfer_size);
this->cur_buf_cnt = transfer_size;
this->msg_buf[0] = ScsiMessage::COMMAND_COMPLETE;
@ -288,20 +281,17 @@ void ScsiHardDisk::write(uint32_t lba, uint16_t transfer_len, uint8_t cmd_len) {
this->incoming_size = transfer_size;
this->hdd_img.seekg(device_offset, this->hdd_img.beg);
this->post_xfer_action = [this]() {
this->hdd_img.write(this->img_buffer, this->incoming_size);
this->post_xfer_action = [this, device_offset]() {
this->hdd_img.write(this->img_buffer, device_offset, this->incoming_size);
};
}
void ScsiHardDisk::seek(uint32_t lba) {
uint64_t device_offset = lba * HDD_SECTOR_SIZE;
this->hdd_img.seekg(device_offset, this->hdd_img.beg);
// No-op
}
void ScsiHardDisk::rewind() {
this->hdd_img.seekg(0, this->hdd_img.beg);
// No-op
}
static const PropMap SCSI_HD_Properties = {

View File

@ -25,9 +25,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#define SCSI_HD_H
#include <devices/common/scsi/scsi.h>
#include <utils/imgfile.h>
#include <cinttypes>
#include <fstream>
#include <memory>
#include <string>
@ -60,7 +60,7 @@ protected:
void rewind();
private:
std::fstream hdd_img;
ImgFile hdd_img;
uint64_t img_size;
int total_blocks;
uint64_t file_offset = 0;

View File

@ -25,13 +25,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <machines/machineproperties.h>
#include <loguru.hpp>
#include <memaccess.h>
#include <utils/imgfile.h>
#include <cinttypes>
#include <cstring>
#include <fstream>
#include <string>
static FlopImgType identify_image(std::ifstream& img_file)
static FlopImgType identify_image(ImgFile& img_file)
{
// WOZ images identification strings
static uint8_t WOZ1_SIG[] = {0x57, 0x4F, 0x5A, 0x31, 0xFF, 0x0A, 0x0D, 0x0A};
@ -39,8 +39,7 @@ static FlopImgType identify_image(std::ifstream& img_file)
uint8_t buf[8] = { 0 };
img_file.seekg(0, std::ios::beg);
img_file.read((char *)buf, sizeof(buf));
img_file.read((char *)buf, 0, sizeof(buf));
// WOZ files are easily identified
if (!std::memcmp(buf, WOZ1_SIG, sizeof(buf))) {
@ -50,8 +49,7 @@ static FlopImgType identify_image(std::ifstream& img_file)
} 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));
img_file.read((char *)buf, 2*BLOCK_SIZE + offset, sizeof(buf));
// check for HFS/MFS signature at the start of the logical block 2
if ((buf[0] == 0x42 && buf[1] == 0x44) ||
@ -79,26 +77,22 @@ RawFloppyImg::RawFloppyImg(std::string& file_path) : FloppyImgConverter()
*/
int RawFloppyImg::calc_phys_params()
{
std::ifstream img_file;
ImgFile img_file;
img_file.open(img_path, std::ios::in | std::ios::binary);
if (img_file.fail()) {
if (!img_file.open(img_path)) {
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);
size_t img_size = img_file.tellg();
size_t img_size = img_file.size();
img_file.close();
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;
img_file.seekg(0, img_file.beg);
img_file.close();
// verify image size
if (this->img_size < 5*BLOCK_SIZE) {
@ -177,17 +171,15 @@ int RawFloppyImg::calc_phys_params()
/** Retrieve raw disk data. */
int RawFloppyImg::get_raw_disk_data(char* buf)
{
std::ifstream img_file;
ImgFile img_file;
img_file.open(img_path, std::ios::in | std::ios::binary);
if (img_file.fail()) {
if (!img_file.open(img_path)) {
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.read(buf, 0, this->data_size);
img_file.close();
return 0;
@ -206,24 +198,20 @@ DiskCopy42Img::DiskCopy42Img(std::string& file_path) : FloppyImgConverter()
}
int DiskCopy42Img::calc_phys_params() {
std::ifstream img_file;
ImgFile img_file;
img_file.open(img_path, std::ios::in | std::ios::binary);
if (img_file.fail()) {
if (!img_file.open(img_path)) {
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);
size_t img_size = img_file.tellg();
img_file.seekg(0, img_file.beg);
size_t img_size = img_file.size();
// get data size from image
uint8_t buf[4];
img_file.seekg(0x40, img_file.beg);
img_file.read((char *)&buf, 4);
img_file.read((char *)&buf, 0x40, 4);
this->data_size = READ_DWORD_BE_U(buf);
if (this->data_size > img_size) {
@ -235,9 +223,8 @@ int DiskCopy42Img::calc_phys_params() {
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.read((char *)&disk_format, 0x50, 1);
img_file.read((char *)&this->format_byte, 0x51, 1);
img_file.close();
@ -272,17 +259,15 @@ int DiskCopy42Img::calc_phys_params() {
}
int DiskCopy42Img::get_raw_disk_data(char* buf) {
std::ifstream img_file;
ImgFile img_file;
img_file.open(img_path, std::ios::in | std::ios::binary);
if (img_file.fail()) {
if (!img_file.open(img_path)) {
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.read(buf, 0x54, this->data_size);
img_file.close();
return 0;
@ -296,10 +281,9 @@ FloppyImgConverter* open_floppy_image(std::string& img_path)
{
FloppyImgConverter *fconv = nullptr;
std::ifstream img_file;
ImgFile img_file;
img_file.open(img_path, std::ios::in | std::ios::binary);
if (img_file.fail()) {
if (!img_file.open(img_path)) {
img_file.close();
LOG_F(ERROR, "Could not open specified floppy image (%s)!", img_path.c_str());
return nullptr;

View File

@ -23,8 +23,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/storage/blockstoragedevice.h>
#include <sys/stat.h>
using namespace std;
BlockStorageDevice::BlockStorageDevice(const uint32_t cache_blocks, const uint32_t block_size) {
@ -36,21 +34,17 @@ BlockStorageDevice::BlockStorageDevice(const uint32_t cache_blocks, const uint32
}
BlockStorageDevice::~BlockStorageDevice() {
if (this->img_file)
this->img_file.close();
this->img_file.close();
}
int BlockStorageDevice::set_host_file(std::string file_path) {
this->is_ready = false;
this->img_file.open(file_path, ios::out | ios::in | ios::binary);
struct stat stat_buf;
int rc = stat(file_path.c_str(), &stat_buf);
if (rc)
if (!this->img_file.open(file_path)) {
return -1;
}
this->size_bytes = stat_buf.st_size;
this->size_bytes = this->img_file.size();
this->size_blocks = this->size_bytes / this->block_size;
this->set_fpos(0);
@ -61,7 +55,6 @@ int BlockStorageDevice::set_host_file(std::string file_path) {
int BlockStorageDevice::set_fpos(const uint32_t lba) {
this->cur_fpos = lba * this->block_size;
this->img_file.seekg(this->cur_fpos, this->img_file.beg);
return 0;
}
@ -75,7 +68,7 @@ int BlockStorageDevice::read_begin(int nblocks, uint32_t max_len) {
this->remain_size = 0;
}
this->img_file.read(this->data_cache.get(), read_size);
this->img_file.read(this->data_cache.get(), this->cur_fpos, read_size);
this->cur_fpos += read_size;
return read_size;
@ -95,7 +88,7 @@ int BlockStorageDevice::read_more() {
this->remain_size = 0;
}
this->img_file.read(this->data_cache.get(), read_size);
this->img_file.read(this->data_cache.get(), this->cur_fpos, read_size);
this->cur_fpos += read_size;
return read_size;

View File

@ -24,8 +24,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef BLOCK_STORAGE_DEVICE_H
#define BLOCK_STORAGE_DEVICE_H
#include <utils/imgfile.h>
#include <cinttypes>
#include <fstream>
#include <memory>
#include <string>
@ -45,7 +46,7 @@ public:
int write_begin(char *buf, int nblocks);
protected:
std::fstream img_file = {};
ImgFile img_file;
uint64_t size_bytes = 0; // image file size in bytes
uint64_t size_blocks = 0; // image file size in blocks
uint64_t cur_fpos = 0; // current image file pointer position

View File

@ -1,6 +1,9 @@
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(PlatformGlob)
include_directories("${PROJECT_SOURCE_DIR}")
file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
platform_glob(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
add_library(utils OBJECT ${SOURCES})
target_link_libraries(utils PRIVATE)

49
utils/imgfile.h Normal file
View File

@ -0,0 +1,49 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-23 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 <https://www.gnu.org/licenses/>.
*/
/** @file Image file abstraction for floppy, hard drive and CD-ROM images
* (implemented on each platform). */
#ifndef IMGFILE_H
#define IMGFILE_H
#include <cstddef>
#include <memory>
#include <string>
class ImgFile {
public:
ImgFile();
~ImgFile();
bool open(const std::string& img_path);
void close();
size_t size() const;
size_t read(void* buf, off_t offset, size_t length) const;
size_t write(const void* buf, off_t offset, size_t length);
private:
class Impl; // Holds private fields
std::unique_ptr<Impl> impl;
};
#endif // IMGFILE_H

67
utils/imgfile_sdl.cpp Normal file
View File

@ -0,0 +1,67 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-23 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 <https://www.gnu.org/licenses/>.
*/
#include <utils/imgfile.h>
#include <fstream>
class ImgFile::Impl {
public:
std::fstream stream;
};
ImgFile::ImgFile(): impl(std::make_unique<Impl>())
{
}
ImgFile::~ImgFile() = default;
bool ImgFile::open(const std::string &img_path)
{
impl->stream.open(img_path, std::ios::in | std::ios::out | std::ios::binary);
return !impl->stream.fail();
}
void ImgFile::close()
{
impl->stream.close();
}
size_t ImgFile::size() const
{
impl->stream.seekg(0, impl->stream.end);
return impl->stream.tellg();
}
size_t ImgFile::read(void* buf, off_t offset, size_t length) const
{
impl->stream.seekg(offset, std::ios::beg);
impl->stream.read((char *)buf, length);
return impl->stream.gcount();
}
size_t ImgFile::write(const void* buf, off_t offset, size_t length)
{
impl->stream.seekg(offset, std::ios::beg);
impl->stream.write((const char *)buf, length);
return impl->stream.gcount();
}