// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** hardisk.c Generic MAME hard disk implementation, with differencing files ***************************************************************************/ #include "harddisk.h" #include "ioprocs.h" #include /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ struct hard_disk_file { chd_file * chd; // CHD file util::random_read_write * fhandle; // file if not a CHD hard_disk_info info; // hard disk info }; /*************************************************************************** CORE IMPLEMENTATION ***************************************************************************/ /*------------------------------------------------- hard_disk_open - open a hard disk handle, given a chd_file -------------------------------------------------*/ hard_disk_file *hard_disk_open(chd_file *chd) { int cylinders, heads, sectors, sectorbytes; hard_disk_file *file; std::string metadata; std::error_condition err; /* punt if no CHD */ if (chd == nullptr) return nullptr; /* read the hard disk metadata */ err = chd->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata); if (err) return nullptr; /* parse the metadata */ if (sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &cylinders, &heads, §ors, §orbytes) != 4) return nullptr; /* allocate memory for the hard disk file */ file = (hard_disk_file *)malloc(sizeof(hard_disk_file)); if (file == nullptr) return nullptr; /* fill in the data */ file->chd = chd; file->fhandle = nullptr; file->info.cylinders = cylinders; file->info.heads = heads; file->info.sectors = sectors; file->info.sectorbytes = sectorbytes; file->info.fileoffset = 0; return file; } hard_disk_file *hard_disk_open(util::random_read_write &corefile, uint32_t skipoffs) { // bail if getting the file length fails std::uint64_t length; if (corefile.length(length)) return nullptr; hard_disk_file *file; // allocate memory for the hard disk file file = (hard_disk_file *)malloc(sizeof(hard_disk_file)); if (file == nullptr) return nullptr; file->chd = nullptr; file->fhandle = &corefile; file->info.sectorbytes = 512; file->info.cylinders = 0; file->info.heads = 0; file->info.sectors = 0; file->info.fileoffset = skipoffs; // attempt to guess geometry in case this is an ATA situation for (uint32_t totalsectors = (length - skipoffs) / file->info.sectorbytes; ; totalsectors++) for (uint32_t cursectors = 63; cursectors > 1; cursectors--) if (totalsectors % cursectors == 0) { uint32_t totalheads = totalsectors / cursectors; for (uint32_t curheads = 16; curheads > 1; curheads--) if (totalheads % curheads == 0) { file->info.cylinders = totalheads / curheads; file->info.heads = curheads; file->info.sectors = cursectors; osd_printf_verbose("Guessed CHS of %d/%d/%d\n", file->info.cylinders, file->info.heads, file->info.sectors); return file; } } return file; } /*------------------------------------------------- hard_disk_close - close a hard disk handle -------------------------------------------------*/ void hard_disk_close(hard_disk_file *file) { if (file->fhandle) file->fhandle->flush(); free(file); } /*------------------------------------------------- hard_disk_get_chd - get a handle to a CHD from a hard disk -------------------------------------------------*/ chd_file *hard_disk_get_chd(hard_disk_file *file) { return file->chd; } /*------------------------------------------------- hard_disk_get_info - return information about a hard disk -------------------------------------------------*/ /** * @fn hard_disk_info *hard_disk_get_info(hard_disk_file *file) * * @brief Hard disk get information. * * @param [in,out] file The hard disk file object to operate on. * * @return null if it fails, else a hard_disk_info*. */ hard_disk_info *hard_disk_get_info(hard_disk_file *file) { return &file->info; } /*------------------------------------------------- hard_disk_read - read sectors from a hard disk -------------------------------------------------*/ /** * @fn bool hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer) * * @brief Hard disk read. * * @param [in,out] file The hard disk file object to operate on. * @param lbasector The sector number (Linear Block Address) to read. * @param buffer The buffer where the hard disk data will be placed. * * @return True if the operation succeeded */ bool hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer) { std::error_condition err; if (file->chd) { err = file->chd->read_units(lbasector, buffer); return !err; } else { size_t actual = 0; err = file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET); if (!err) err = file->fhandle->read(buffer, file->info.sectorbytes, actual); return !err && (actual == file->info.sectorbytes); } } /*------------------------------------------------- hard_disk_write - write sectors to a hard disk -------------------------------------------------*/ /** * @fn bool hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer) * * @brief Hard disk write. * * @param [in,out] file The hard disk file object to operate on. * @param lbasector The sector number (Linear Block Address) to write. * @param buffer The buffer containing the data to write. * * @return True if the operation succeeded */ bool hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer) { std::error_condition err; if (file->chd) { err = file->chd->write_units(lbasector, buffer); return !err; } else { size_t actual = 0; err = file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET); if (!err) err = file->fhandle->write(buffer, file->info.sectorbytes, actual); return !err && (actual == file->info.sectorbytes); } } /*------------------------------------------------- hard_disk_set_block_size - sets the block size for a non-CHD-backed hard disk (a bare file). -------------------------------------------------*/ /** * @fn bool hard_disk_set_block_size(hard_disk_file *file, uint32_t blocksize) * * @brief Hard disk set block size (works only for non-CHD-files) * * @param [in,out] file The hard_disk_file object to operate on. * @param blocksize The block size of this hard disk, in bytes. * * @return true on success, false on failure. Failure means a CHD is in use and the CHD * block size does not match the passed in size. If a CHD is in use and the block * sizes match, success is returned). */ bool hard_disk_set_block_size(hard_disk_file *file, uint32_t blocksize) { if (file->chd) { // if the CHD block size matches our block size, we're OK. if (file->chd->unit_bytes() == blocksize) { return true; } // indicate failure, since we can't change the block size of a CHD. return false; } else { file->info.sectorbytes = blocksize; return true; } }