/**
* ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
*
* This file is licensed under the GPL version 3 or any later version.
* It is derived from disk.c in SCSI2SD V6
*
* https://www.gnu.org/licenses/gpl-3.0.html
* ----
* 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 .
**/
#include "ImageBackingStore.h"
#include
#include
#include "BlueSCSI_log.h"
#include "BlueSCSI_config.h"
#include
#include
#include
#include
ImageBackingStore::ImageBackingStore()
{
m_israw = false;
m_isrom = false;
m_isreadonly_attr = false;
m_blockdev = nullptr;
m_bgnsector = m_endsector = m_cursector = 0;
}
ImageBackingStore::ImageBackingStore(const char *filename, uint32_t scsi_block_size): ImageBackingStore()
{
if (strncasecmp(filename, "RAW:", 4) == 0)
{
char *endptr, *endptr2;
m_bgnsector = strtoul(filename + 4, &endptr, 0);
m_endsector = strtoul(endptr + 1, &endptr2, 0);
if (*endptr != ':' || *endptr2 != '\0')
{
log("Invalid format for raw filename: ", filename);
return;
}
if ((scsi_block_size % SD_SECTOR_SIZE) != 0)
{
log("SCSI block size ", (int)scsi_block_size, " is not supported for RAW partitions (must be divisible by 512 bytes)");
return;
}
m_israw = true;
m_blockdev = SD.card();
uint32_t sectorCount = SD.card()->sectorCount();
if (m_endsector >= sectorCount)
{
log("Limiting RAW image mapping to SD card sector count: ", (int)sectorCount);
m_endsector = sectorCount - 1;
}
}
else if (strncasecmp(filename, "ROM:", 4) == 0)
{
if (!romDriveCheckPresent(&m_romhdr))
{
m_romhdr.imagesize = 0;
}
else
{
m_isrom = true;
}
}
else
{
m_isreadonly_attr = !!(FS_ATTRIB_READ_ONLY & SD.attrib(filename));
if (m_isreadonly_attr)
{
m_fsfile = SD.open(filename, O_RDONLY);
log("---- Image file is read-only, writes disabled");
}
else
{
m_fsfile = SD.open(filename, O_RDWR);
}
uint32_t sectorcount = m_fsfile.size() / SD_SECTOR_SIZE;
uint32_t begin = 0, end = 0;
if (m_fsfile.contiguousRange(&begin, &end) && end >= begin + sectorcount
&& (scsi_block_size % SD_SECTOR_SIZE) == 0)
{
// Convert to raw mapping, this avoids some unnecessary
// access overhead in SdFat library.
// If non-aligned offsets are later requested, it automatically falls
// back to SdFat access mode.
m_israw = true;
m_blockdev = SD.card();
m_bgnsector = begin;
m_endsector = begin + sectorcount - 1;
m_fsfile.flush(); // Note: m_fsfile is also kept open as a fallback.
}
}
}
bool ImageBackingStore::isOpen()
{
if (m_israw)
return (m_blockdev != NULL);
else if (m_isrom)
return (m_romhdr.imagesize > 0);
else
return m_fsfile.isOpen();
}
bool ImageBackingStore::isWritable()
{
return !(m_isrom && m_isreadonly_attr);
}
bool ImageBackingStore::isRom()
{
return m_isrom;
}
bool ImageBackingStore::isRaw()
{
return m_israw;
}
bool ImageBackingStore::close()
{
if (m_israw)
{
m_blockdev = nullptr;
return true;
}
else if (m_isrom)
{
m_romhdr.imagesize = 0;
return true;
}
else
{
return m_fsfile.close();
}
}
uint64_t ImageBackingStore::size()
{
if (m_israw && m_blockdev)
{
return (uint64_t)(m_endsector - m_bgnsector + 1) * SD_SECTOR_SIZE;
}
else if (m_isrom)
{
return m_romhdr.imagesize;
}
else
{
return m_fsfile.size();
}
}
bool ImageBackingStore::contiguousRange(uint32_t* bgnSector, uint32_t* endSector)
{
if (m_israw && m_blockdev)
{
*bgnSector = m_bgnsector;
*endSector = m_endsector;
return true;
}
else if (m_isrom)
{
*bgnSector = 0;
*endSector = 0;
return true;
}
else
{
return m_fsfile.contiguousRange(bgnSector, endSector);
}
}
bool ImageBackingStore::seek(uint64_t pos)
{
uint32_t sectornum = pos / SD_SECTOR_SIZE;
if (m_israw && (uint64_t)sectornum * SD_SECTOR_SIZE != pos)
{
debuglog("---- Unaligned access to image, falling back to SdFat access mode");
m_israw = false;
}
if (m_israw)
{
m_cursector = m_bgnsector + sectornum;
return (m_cursector <= m_endsector);
}
else if (m_isrom)
{
uint32_t sectornum = pos / SD_SECTOR_SIZE;
assert((uint64_t)sectornum * SD_SECTOR_SIZE == pos);
m_cursector = sectornum;
return m_cursector * SD_SECTOR_SIZE < m_romhdr.imagesize;
}
else
{
return m_fsfile.seek(pos);
}
}
ssize_t ImageBackingStore::read(void* buf, size_t count)
{
uint32_t sectorcount = count / SD_SECTOR_SIZE;
if (m_israw && (uint64_t)sectorcount * SD_SECTOR_SIZE != count)
{
debuglog("---- Unaligned access to image, falling back to SdFat access mode");
m_israw = false;
}
if (m_israw && m_blockdev)
{
if (m_blockdev->readSectors(m_cursector, (uint8_t*)buf, sectorcount))
{
m_cursector += sectorcount;
return count;
}
else
{
return -1;
}
}
else if (m_isrom)
{
assert((uint64_t)sectorcount * SD_SECTOR_SIZE == count);
uint32_t start = m_cursector * SD_SECTOR_SIZE;
if (romDriveRead((uint8_t*)buf, start, count))
{
m_cursector += sectorcount;
return count;
}
else
{
return -1;
}
}
else
{
return m_fsfile.read(buf, count);
}
}
ssize_t ImageBackingStore::write(const void* buf, size_t count)
{
uint32_t sectorcount = count / SD_SECTOR_SIZE;
if (m_israw && (uint64_t)sectorcount * SD_SECTOR_SIZE != count)
{
debuglog("---- Unaligned access to image, falling back to SdFat access mode");
m_israw = false;
}
if (m_israw && m_blockdev)
{
if (m_blockdev->writeSectors(m_cursector, (const uint8_t*)buf, sectorcount))
{
m_cursector += sectorcount;
return count;
}
else
{
return 0;
}
}
else if (m_isrom)
{
log("ERROR: attempted to write to ROM drive");
return 0;
}
else if (m_isreadonly_attr)
{
log("ERROR: attempted to write to a read only image");
return 0;
}
else
{
return m_fsfile.write(buf, count);
}
}
void ImageBackingStore::flush()
{
if (!m_israw && !m_isrom && !m_isreadonly_attr)
{
m_fsfile.flush();
}
}
void ImageBackingStore::getName(char * name, size_t len)
{
if(m_isrom)
name = (char*)"ROM:";
else if(m_israw)
name = (char*)"RAW:";
else
m_fsfile.getName(name, len);
}
uint64_t ImageBackingStore::position()
{
if (!m_israw && !m_isrom)
{
return m_fsfile.curPosition();
}
else
{
return 0;
}
}