Add ramdisk preloaded from RAW/VHD image file (*.rdimg;*.rdvhd) (#275)

This commit is contained in:
Matt (MMaster)
2024-11-25 05:28:58 +01:00
committed by GitHub
parent 803ccef13f
commit 1882f179e0
7 changed files with 490 additions and 11 deletions

View File

@@ -4,6 +4,9 @@
- Numerous bugfixes in this build
- Changed some GUI elements.
- Preliminary Plugin Extensions Created
- Added RAM disk preloaded with RAW/VHD images (*.rdimg;*.rdvhd)
- Load up to 2GB disks to temporary disk (doesn't modify the image file)
- Mounts as read-only if memory allocation fails (mind the 32bit PCem memory limits)
## Added the following machines to v18
- Hyundai SUPER-16T

View File

@@ -3,6 +3,7 @@
typedef enum hdd_img_type {
HDD_IMG_RAW,
HDD_IMG_VHD,
HDD_IMG_RAW_RAM,
} hdd_img_type;
typedef struct hdd_file_t {

View File

@@ -0,0 +1,95 @@
#ifndef __HAVE_RAMDISK_H__
#define __HAVE_RAMDISK_H__
/*
* Growable RAM disk memory stream for PCem by MMaster (2024)
*
* - Automatically grows memory buffer (disk size)
* - Maintains expected size separate from allocated size
* - Only writes and set_size calls grow memory
* - Allows preload from raw image file
* - Behaves similarly to fread, fwrite & fseek
*
* Note: Maximum size is 2GB - 1B
*/
/* ramdisk context */
typedef struct ramdisk ramdisk_t;
/**
* Create new ramdisk.
*/
ramdisk_t *ramdisk_init();
/**
* Release ramdisk memory.
*
* @param ramdisk Ramdisk context
*/
void ramdisk_free(ramdisk_t *ramdisk);
/**
* Set required size of the disk.
*
* @note Disk is still growable with writes beyond end.
* @param ramdisk Ramdisk context
* @param size New size of the disk
* @raturn 0 on success, -1 if unable to resize
*/
int ramdisk_set_size(ramdisk_t *ramdisk, size_t size);
/**
* Write data to ramdisk at cursor.
*
* @note Data may be written beyond the current size of the disk.
* @param ramdisk Ramdisk context
* @param buf Buffer containing data to be written
* @param size Size of the buffer
* @return 0 on EOF, -1 on error (sets errno), >0 number of bytes written
*/
int ramdisk_write(ramdisk_t *ramdisk, const char *buf, size_t size);
/**
* Read data from ramdisk at cursor.
*
* @param ramdisk Ramdisk context
* @param buf Buffer for data to be read to
* @param size Size of the buffer
* @return 0 on EOF, -1 on error (sets errno), >0 number of bytes read
*/
int ramdisk_read(ramdisk_t *ramdisk, char *buf, size_t size);
/**
* Seek within the ramdisk. Moves the cursor.
*
* @param ramdisk Ramdisk context
* @param offset Offset from position specified by "whence" parameter.
* @param whence SEEK_SET, SEEK_CUR or SEEK_END
* @return >=0 disk cursor offset, -1 on error (sets errno)
*/
int ramdisk_seek(ramdisk_t *ramdisk, off_t offset, int whence);
/**
* Get temporary pointer to buffer from current cursor in ramdisk.
*
* Warning: This pointer is volatile and may be invalidated by write and set size calls.
* Do not try to free this buffer!
*
* @param ramdisk Ramdisk context
* @param mem Pointer to memory pointer that will be set to ramdisk buffer.
* @param size Pointer to size of the buffer
* @return 0 on EOF, -1 on error (cursor outside of allocated memory)
*/
int ramdisk_get_cursor_mem(ramdisk_t *ramdisk, char **mem, size_t *size);
/**
* Load data from file to ramdisk at cursor.
*
* @note Data may be written beyond the current size of the disk.
* @param ramdisk Ramdisk context
* @param fp raw image FILE pointer
* @return 0 on EOF, -1 on error (sets errno), >0 number of bytes written
*/
int ramdisk_load_file(ramdisk_t *ramdisk, FILE *fp);
#endif /* !__HAVE_RAMDISK_H__ */

View File

@@ -2,6 +2,7 @@ set(PCEM_PRIVATE_API ${PCEM_PRIVATE_API}
${CMAKE_SOURCE_DIR}/includes/private/hdd/hdd_esdi.h
${CMAKE_SOURCE_DIR}/includes/private/hdd/hdd_file.h
${CMAKE_SOURCE_DIR}/includes/private/hdd/hdd.h
${CMAKE_SOURCE_DIR}/includes/private/hdd/ramdisk/ramdisk.h
${CMAKE_SOURCE_DIR}/includes/private/hdd/minivhd/cwalk.h
${CMAKE_SOURCE_DIR}/includes/private/hdd/minivhd/libxml2_encoding.h
${CMAKE_SOURCE_DIR}/includes/private/hdd/minivhd/minivhd_create.h
@@ -18,6 +19,11 @@ set(PCEM_SRC ${PCEM_SRC}
hdd/hdd_file.c
)
# RAMDisk
set(PCEM_SRC ${PCEM_SRC}
hdd/ramdisk/ramdisk.c
)
# MiniVHD
set(PCEM_SRC ${PCEM_SRC}
hdd/minivhd/cwalk.c

View File

@@ -5,10 +5,30 @@
#include "ibm.h"
#include "hdd_file.h"
#include "ramdisk/ramdisk.h"
#include "minivhd/minivhd.h"
#include "minivhd/minivhd_util.h"
bool is_ramdisk_file(const char *fn) {
const char *ext1 = ".rdimg";
const char *ext2 = ".rdvhd";
int ext_len = 6;
int len = strlen(fn);
if (len < ext_len)
return false;
const char *extp = fn + (len - ext_len);
return (strncmp(extp, ext1, ext_len) == 0) ||
(strncmp(extp, ext2, ext_len) == 0);
}
void hdd_load_ext(hdd_file_t *hdd, const char *fn, int spt, int hpc, int tracks, int read_only) {
int requested_read_only = read_only;
bool is_ramdisk = is_ramdisk_file(fn);
if (is_ramdisk)
read_only = 1;
if (hdd->f == NULL) {
/* Try to open existing hard disk image */
if (read_only)
@@ -17,6 +37,7 @@ void hdd_load_ext(hdd_file_t *hdd, const char *fn, int spt, int hpc, int tracks,
hdd->f = (void *)fopen64(fn, "rb+");
if (hdd->f != NULL) {
hdd->img_type = HDD_IMG_RAW;
/* Check if the file we opened is a VHD */
if (mvhd_file_is_vhd((FILE *)hdd->f)) {
int err;
@@ -72,6 +93,58 @@ void hdd_load_ext(hdd_file_t *hdd, const char *fn, int spt, int hpc, int tracks,
}
hdd->sectors = hdd->spt * hdd->hpc * hdd->tracks;
hdd->read_only = read_only;
if (is_ramdisk) {
ramdisk_t *ramdisk = ramdisk_init();
if (ramdisk == NULL) {
pclog("Cannot initialize ramdisk '%s' : %s", fn, strerror(errno));
return;
}
// prepare ramdisk buffer to have enough space for whole raw image
size_t size = hdd->sectors * 512 + 1;
if (ramdisk_set_size(ramdisk, size) < 0) {
ramdisk_free(ramdisk);
pclog("Cannot set ramdisk '%s' size to %d: %s", fn, size, strerror(errno));
return;
}
if (hdd->img_type == HDD_IMG_RAW) {
if (ramdisk_load_file(ramdisk, (FILE *)hdd->f) < 0) {
pclog("Cannot load ramdisk from file '%s' : %s", fn, strerror(errno));
ramdisk_free(ramdisk);
return;
}
fclose((FILE *)hdd->f);
} else if (hdd->img_type == HDD_IMG_VHD) {
char *rd_buf;
size_t rd_buf_size;
// get temporary buffer from beginning of disk (cursor is at 0)
if (ramdisk_get_cursor_mem(ramdisk, &rd_buf, &rd_buf_size) < 0) {
pclog("Unable to get ramdisk cursor memory pointer '%s'", fn);
ramdisk_free(ramdisk);
return;
}
// immediately read all sectors from VHD to our raw ramdisk memory
if (mvhd_read_sectors((MVHDMeta *)hdd->f, 0, hdd->sectors, rd_buf) < 0) {
pclog("Unable to read VHD image sectors to ramdisk '%s'", fn);
ramdisk_free(ramdisk);
return;
}
mvhd_close((MVHDMeta *)hdd->f);
} else {
pclog("Unsupported HDD image type for ramdisk '%s'", fn);
ramdisk_free(ramdisk);
return;
}
hdd->f = (void *)ramdisk;
hdd->img_type = HDD_IMG_RAW_RAM;
hdd->read_only = requested_read_only;
}
}
void hdd_load(hdd_file_t *hdd, int d, const char *fn) { hdd_load_ext(hdd, fn, hdc[d].spt, hdc[d].hpc, hdc[d].tracks, 0); }
@@ -82,6 +155,8 @@ void hdd_close(hdd_file_t *hdd) {
mvhd_close((MVHDMeta *)hdd->f);
else if (hdd->img_type == HDD_IMG_RAW)
fclose((FILE *)hdd->f);
else if (hdd->img_type == HDD_IMG_RAW_RAM)
ramdisk_free((ramdisk_t *)hdd->f);
}
hdd->img_type = HDD_IMG_RAW;
hdd->f = NULL;
@@ -90,7 +165,8 @@ void hdd_close(hdd_file_t *hdd) {
int hdd_read_sectors(hdd_file_t *hdd, int offset, int nr_sectors, void *buffer) {
if (hdd->img_type == HDD_IMG_VHD) {
return mvhd_read_sectors((MVHDMeta *)hdd->f, offset, nr_sectors, buffer);
} else if (hdd->img_type == HDD_IMG_RAW) {
} else if (hdd->img_type == HDD_IMG_RAW ||
hdd->img_type == HDD_IMG_RAW_RAM) {
off64_t addr;
int transfer_sectors = nr_sectors;
@@ -98,8 +174,15 @@ int hdd_read_sectors(hdd_file_t *hdd, int offset, int nr_sectors, void *buffer)
transfer_sectors = hdd->sectors - offset;
addr = (uint64_t)offset * 512;
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
fread(buffer, transfer_sectors * 512, 1, (FILE *)hdd->f);
if (hdd->img_type == HDD_IMG_RAW) {
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
fread(buffer, transfer_sectors * 512, 1, (FILE *)hdd->f);
} else if (hdd->img_type == HDD_IMG_RAW_RAM) {
ramdisk_t *ramdisk = (ramdisk_t *)hdd->f;
ramdisk_seek(ramdisk, addr, SEEK_SET);
ramdisk_read(ramdisk, buffer, transfer_sectors * 512);
} else
return 1;
if (nr_sectors != transfer_sectors)
return 1;
@@ -112,7 +195,8 @@ int hdd_read_sectors(hdd_file_t *hdd, int offset, int nr_sectors, void *buffer)
int hdd_write_sectors(hdd_file_t *hdd, int offset, int nr_sectors, void *buffer) {
if (hdd->img_type == HDD_IMG_VHD) {
return mvhd_write_sectors((MVHDMeta *)hdd->f, offset, nr_sectors, buffer);
} else if (hdd->img_type == HDD_IMG_RAW) {
} else if (hdd->img_type == HDD_IMG_RAW ||
hdd->img_type == HDD_IMG_RAW_RAM) {
off64_t addr;
int transfer_sectors = nr_sectors;
@@ -123,8 +207,15 @@ int hdd_write_sectors(hdd_file_t *hdd, int offset, int nr_sectors, void *buffer)
transfer_sectors = hdd->sectors - offset;
addr = (uint64_t)offset * 512;
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
fwrite(buffer, transfer_sectors * 512, 1, (FILE *)hdd->f);
if (hdd->img_type == HDD_IMG_RAW) {
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
fwrite(buffer, transfer_sectors * 512, 1, (FILE *)hdd->f);
} else if (hdd->img_type == HDD_IMG_RAW_RAM) {
ramdisk_t *ramdisk = (ramdisk_t *)hdd->f;
ramdisk_seek(ramdisk, addr, SEEK_SET);
ramdisk_write(ramdisk, buffer, transfer_sectors * 512);
} else
return 1;
if (nr_sectors != transfer_sectors)
return 1;
@@ -137,7 +228,8 @@ int hdd_write_sectors(hdd_file_t *hdd, int offset, int nr_sectors, void *buffer)
int hdd_format_sectors(hdd_file_t *hdd, int offset, int nr_sectors) {
if (hdd->img_type == HDD_IMG_VHD) {
return mvhd_format_sectors((MVHDMeta *)hdd->f, offset, nr_sectors);
} else if (hdd->img_type == HDD_IMG_RAW) {
} else if (hdd->img_type == HDD_IMG_RAW ||
hdd->img_type == HDD_IMG_RAW_RAM) {
off64_t addr;
int c;
uint8_t zero_buffer[512];
@@ -151,9 +243,18 @@ int hdd_format_sectors(hdd_file_t *hdd, int offset, int nr_sectors) {
if ((hdd->sectors - offset) < transfer_sectors)
transfer_sectors = hdd->sectors - offset;
addr = (uint64_t)offset * 512;
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
for (c = 0; c < transfer_sectors; c++)
fwrite(zero_buffer, 512, 1, (FILE *)hdd->f);
if (hdd->img_type == HDD_IMG_RAW) {
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
for (c = 0; c < transfer_sectors; c++)
fwrite(zero_buffer, 512, 1, (FILE *)hdd->f);
} else if (hdd->img_type == HDD_IMG_RAW_RAM) {
ramdisk_t *ramdisk = (ramdisk_t *)hdd->f;
ramdisk_seek(ramdisk, addr, SEEK_SET);
for (c = 0; c < transfer_sectors; c++)
ramdisk_write(ramdisk, zero_buffer, 512);
} else
return 1;
if (nr_sectors != transfer_sectors)
return 1;

273
src/hdd/ramdisk/ramdisk.c Normal file
View File

@@ -0,0 +1,273 @@
/*
* Growable RAM disk memory stream for PCem by MMaster (2024)
* Inspired by fmem https://github.com/Snaipe/fmem
*/
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define KB_SIZE * 1024
#define MB_SIZE * (1024 KB_SIZE)
#define GB_SIZE * (1024 MB_SIZE)
// Max: 2 GB - 1B regardless of 32 / 64 bit
#define MAX_STREAM_SIZE (-1 + 1 GB_SIZE + 1 GB_SIZE)
/* Stream section */
typedef struct ramdisk_stream {
uint8_t *mem;
size_t _size; // internal allocation size
size_t size; // expected size
size_t cursor;
} ramdisk_stream_t;
ramdisk_stream_t *ramdisk_stream_init() {
ramdisk_stream_t *stream = (ramdisk_stream_t *)malloc(sizeof(ramdisk_stream_t));
if (stream == NULL)
return NULL;
memset(stream, 0, sizeof(*stream));
stream->_size = 4 KB_SIZE;
stream->mem = (uint8_t *)malloc(stream->_size);
if (stream->mem == NULL) {
free(stream);
return NULL;
}
return stream;
}
void ramdisk_stream_free(ramdisk_stream_t *stream) {
free(stream->mem);
free(stream);
}
size_t golden_growth_ceil(size_t n)
{
/* This effectively is a return ceil(n * φ).
φ is approximatively 207 / (2^7), so we shift our result by
6, then perform our ceil by adding the remainder of the last division
by 2 of the result to itself. */
n = (n * 207) >> 6;
n = (n >> 1) + (n & 1);
return n;
}
int ramdisk_stream_grow(ramdisk_stream_t *stream, size_t required) {
if (stream->cursor > MAX_STREAM_SIZE - required) {
errno = EOVERFLOW;
return -1;
}
required += stream->cursor;
size_t newsize = stream->_size;
if (required <= newsize)
return 0;
while (required > newsize) {
newsize = golden_growth_ceil(newsize);
}
if (newsize > MAX_STREAM_SIZE)
newsize = MAX_STREAM_SIZE;
uint8_t *newmem = realloc(stream->mem, newsize);
if (newmem == NULL && newsize > required)
newmem = realloc(stream->mem, required);
if (newmem == NULL) {
errno = ENOMEM;
return -1;
}
stream->mem = newmem;
stream->_size = newsize;
return 0;
}
int ramdisk_stream_resize(ramdisk_stream_t *stream, size_t size) {
if (size <= stream->_size)
return 0;
if (size > MAX_STREAM_SIZE) {
errno = EOVERFLOW;
return -1;
}
uint8_t *newmem = realloc(stream->mem, size);
if (newmem == NULL) {
errno = ENOMEM;
return -1;
}
stream->mem = newmem;
stream->_size = size;
return 0;
}
// get buffer for reading (not incl. allocated space outside of current size)
int ramdisk_stream_cursor_buf_r(ramdisk_stream_t *stream, uint8_t **buf, size_t *buf_size) {
if (stream->size < stream->cursor)
return -1;
*buf_size = stream->size - stream->cursor;
*buf = stream->mem + stream->cursor;
return 0;
}
// get buffer for writing (incl. allocated space outside of current size)
int ramdisk_stream_cursor_buf_w(ramdisk_stream_t *stream, uint8_t **buf, size_t *buf_size) {
if (stream->_size < stream->cursor)
return -1;
*buf_size = stream->_size - stream->cursor;
*buf = stream->mem + stream->cursor;
return 0;
}
size_t ramdisk_stream_copy_buf(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size) {
size_t len = src_size < dst_size ? src_size : dst_size;
memcpy(dst, src, len);
return len;
}
/* Disk section */
typedef struct ramdisk {
ramdisk_stream_t *stream;
} ramdisk_t;
ramdisk_t *ramdisk_init() {
ramdisk_t *ramdisk = (ramdisk_t *)malloc(sizeof(ramdisk_t));
if (ramdisk == NULL)
return NULL;
ramdisk->stream = ramdisk_stream_init();
if (ramdisk->stream == NULL) {
free(ramdisk);
return NULL;
}
return ramdisk;
}
void ramdisk_free(ramdisk_t *ramdisk) {
ramdisk_stream_free(ramdisk->stream);
free(ramdisk);
}
int ramdisk_set_size(ramdisk_t *ramdisk, size_t size) {
if (size > ramdisk->stream->_size) {
if (ramdisk_stream_resize(ramdisk->stream, size) < 0)
return -1;
}
ramdisk->stream->size = size;
return 0;
}
int ramdisk_write(ramdisk_t *ramdisk, const char *buf, size_t size) {
if (ramdisk->stream->cursor > MAX_STREAM_SIZE - size)
size = MAX_STREAM_SIZE - ramdisk->stream->cursor;
char *dst_buf;
size_t dst_buf_size;
if (ramdisk_stream_grow(ramdisk->stream, size) < 0)
return -1;
if (ramdisk_stream_cursor_buf_w(ramdisk->stream, (uint8_t **)&dst_buf, &dst_buf_size) < 0)
return 0;
size_t written = ramdisk_stream_copy_buf(dst_buf, dst_buf_size, buf, size);
if (written > INT_MAX) {
errno = EOVERFLOW;
return -1;
}
ramdisk->stream->cursor += written;
// if it was written beyond current 'end of file' move the EOF
if (ramdisk->stream->size < ramdisk->stream->cursor)
ramdisk->stream->size = ramdisk->stream->cursor;
return written;
}
int ramdisk_read(ramdisk_t *ramdisk, char *buf, size_t size) {
if (ramdisk->stream->cursor > MAX_STREAM_SIZE - size)
size = MAX_STREAM_SIZE - ramdisk->stream->cursor;
// end of file at MAX_STREAM_SIZE
if (size == 0)
return 0;
char *src_buf;
size_t src_buf_size;
size_t written = 0;
if (ramdisk_stream_cursor_buf_r(ramdisk->stream, (uint8_t **)&src_buf, &src_buf_size) == 0) {
if (src_buf_size == 0)
return 0; // eof
written = ramdisk_stream_copy_buf(buf, size, src_buf, src_buf_size);
}
if (written > INT_MAX) {
errno = EOVERFLOW;
return -1;
}
ramdisk->stream->cursor += written;
return written;
}
int ramdisk_seek(ramdisk_t *ramdisk, off_t offset, int whence) {
size_t new_offset;
switch (whence) {
case SEEK_SET:
new_offset = offset;
break;
case SEEK_CUR:
new_offset = ramdisk->stream->cursor + offset;
break;
case SEEK_END:
new_offset = ramdisk->stream->size + offset;
break;
default:
errno = EINVAL;
return -1;
}
if (new_offset < 0 || new_offset > MAX_STREAM_SIZE) {
errno = EOVERFLOW;
return -1;
}
ramdisk->stream->cursor = new_offset;
return new_offset;
}
int ramdisk_get_cursor_mem(ramdisk_t *ramdisk, char **mem, size_t *size) {
return ramdisk_stream_cursor_buf_w(ramdisk->stream, (uint8_t **)mem, size);
}
int ramdisk_load_file(ramdisk_t *ramdisk, FILE *fp) {
// get file size
fseek(fp, 0, SEEK_END);
size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (ramdisk_stream_resize(ramdisk->stream, size) < 0)
return -1;
ramdisk_seek(ramdisk, 0, SEEK_SET);
char *buf;
size_t buf_size;
if (ramdisk_stream_cursor_buf_w(ramdisk->stream, (uint8_t **)&buf, &buf_size) < 0)
return -1;
size_t len = fread(buf, 1, buf_size, fp);
if (len > 0)
ramdisk->stream->size = ramdisk->stream->cursor + len;
return len;
}

View File

@@ -2036,7 +2036,7 @@ static int hd_new(void *hdlg, int drive) {
}
static int hd_file(void *hdlg, int drive) {
if (!getfile(hdlg, "Hard disc image (*.img;*.vhd)|*.img;*.vhd|All files (*.*)|*.*", "")) {
if (!getfile(hdlg, "Hard disk image (*.img;*.vhd)|*.img;*.vhd|RAM disk image (*.rdimg;*.rdvhd)|*.rdimg;*.rdvhd|All files (*.*)|*.*", "")) {
off_t sz;
FILE *f = fopen64(openfilestring, "rb");
if (!f) {