mirror of
https://github.com/sarah-walker-pcem/pcem.git
synced 2025-07-23 11:43:03 +02:00
Add ramdisk preloaded from RAW/VHD image file (*.rdimg;*.rdvhd) (#275)
This commit is contained in:
@@ -4,6 +4,9 @@
|
|||||||
- Numerous bugfixes in this build
|
- Numerous bugfixes in this build
|
||||||
- Changed some GUI elements.
|
- Changed some GUI elements.
|
||||||
- Preliminary Plugin Extensions Created
|
- 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
|
## Added the following machines to v18
|
||||||
- Hyundai SUPER-16T
|
- Hyundai SUPER-16T
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
typedef enum hdd_img_type {
|
typedef enum hdd_img_type {
|
||||||
HDD_IMG_RAW,
|
HDD_IMG_RAW,
|
||||||
HDD_IMG_VHD,
|
HDD_IMG_VHD,
|
||||||
|
HDD_IMG_RAW_RAM,
|
||||||
} hdd_img_type;
|
} hdd_img_type;
|
||||||
|
|
||||||
typedef struct hdd_file_t {
|
typedef struct hdd_file_t {
|
||||||
|
95
includes/private/hdd/ramdisk/ramdisk.h
Normal file
95
includes/private/hdd/ramdisk/ramdisk.h
Normal 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__ */
|
||||||
|
|
@@ -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_esdi.h
|
||||||
${CMAKE_SOURCE_DIR}/includes/private/hdd/hdd_file.h
|
${CMAKE_SOURCE_DIR}/includes/private/hdd/hdd_file.h
|
||||||
${CMAKE_SOURCE_DIR}/includes/private/hdd/hdd.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/cwalk.h
|
||||||
${CMAKE_SOURCE_DIR}/includes/private/hdd/minivhd/libxml2_encoding.h
|
${CMAKE_SOURCE_DIR}/includes/private/hdd/minivhd/libxml2_encoding.h
|
||||||
${CMAKE_SOURCE_DIR}/includes/private/hdd/minivhd/minivhd_create.h
|
${CMAKE_SOURCE_DIR}/includes/private/hdd/minivhd/minivhd_create.h
|
||||||
@@ -18,6 +19,11 @@ set(PCEM_SRC ${PCEM_SRC}
|
|||||||
hdd/hdd_file.c
|
hdd/hdd_file.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# RAMDisk
|
||||||
|
set(PCEM_SRC ${PCEM_SRC}
|
||||||
|
hdd/ramdisk/ramdisk.c
|
||||||
|
)
|
||||||
|
|
||||||
# MiniVHD
|
# MiniVHD
|
||||||
set(PCEM_SRC ${PCEM_SRC}
|
set(PCEM_SRC ${PCEM_SRC}
|
||||||
hdd/minivhd/cwalk.c
|
hdd/minivhd/cwalk.c
|
||||||
|
@@ -5,10 +5,30 @@
|
|||||||
|
|
||||||
#include "ibm.h"
|
#include "ibm.h"
|
||||||
#include "hdd_file.h"
|
#include "hdd_file.h"
|
||||||
|
#include "ramdisk/ramdisk.h"
|
||||||
#include "minivhd/minivhd.h"
|
#include "minivhd/minivhd.h"
|
||||||
#include "minivhd/minivhd_util.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) {
|
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) {
|
if (hdd->f == NULL) {
|
||||||
/* Try to open existing hard disk image */
|
/* Try to open existing hard disk image */
|
||||||
if (read_only)
|
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+");
|
hdd->f = (void *)fopen64(fn, "rb+");
|
||||||
if (hdd->f != NULL) {
|
if (hdd->f != NULL) {
|
||||||
hdd->img_type = HDD_IMG_RAW;
|
hdd->img_type = HDD_IMG_RAW;
|
||||||
|
|
||||||
/* Check if the file we opened is a VHD */
|
/* Check if the file we opened is a VHD */
|
||||||
if (mvhd_file_is_vhd((FILE *)hdd->f)) {
|
if (mvhd_file_is_vhd((FILE *)hdd->f)) {
|
||||||
int err;
|
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->sectors = hdd->spt * hdd->hpc * hdd->tracks;
|
||||||
hdd->read_only = read_only;
|
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); }
|
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);
|
mvhd_close((MVHDMeta *)hdd->f);
|
||||||
else if (hdd->img_type == HDD_IMG_RAW)
|
else if (hdd->img_type == HDD_IMG_RAW)
|
||||||
fclose((FILE *)hdd->f);
|
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->img_type = HDD_IMG_RAW;
|
||||||
hdd->f = NULL;
|
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) {
|
int hdd_read_sectors(hdd_file_t *hdd, int offset, int nr_sectors, void *buffer) {
|
||||||
if (hdd->img_type == HDD_IMG_VHD) {
|
if (hdd->img_type == HDD_IMG_VHD) {
|
||||||
return mvhd_read_sectors((MVHDMeta *)hdd->f, offset, nr_sectors, buffer);
|
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;
|
off64_t addr;
|
||||||
int transfer_sectors = nr_sectors;
|
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;
|
transfer_sectors = hdd->sectors - offset;
|
||||||
addr = (uint64_t)offset * 512;
|
addr = (uint64_t)offset * 512;
|
||||||
|
|
||||||
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
|
if (hdd->img_type == HDD_IMG_RAW) {
|
||||||
fread(buffer, transfer_sectors * 512, 1, (FILE *)hdd->f);
|
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)
|
if (nr_sectors != transfer_sectors)
|
||||||
return 1;
|
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) {
|
int hdd_write_sectors(hdd_file_t *hdd, int offset, int nr_sectors, void *buffer) {
|
||||||
if (hdd->img_type == HDD_IMG_VHD) {
|
if (hdd->img_type == HDD_IMG_VHD) {
|
||||||
return mvhd_write_sectors((MVHDMeta *)hdd->f, offset, nr_sectors, buffer);
|
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;
|
off64_t addr;
|
||||||
int transfer_sectors = nr_sectors;
|
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;
|
transfer_sectors = hdd->sectors - offset;
|
||||||
addr = (uint64_t)offset * 512;
|
addr = (uint64_t)offset * 512;
|
||||||
|
|
||||||
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
|
if (hdd->img_type == HDD_IMG_RAW) {
|
||||||
fwrite(buffer, transfer_sectors * 512, 1, (FILE *)hdd->f);
|
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)
|
if (nr_sectors != transfer_sectors)
|
||||||
return 1;
|
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) {
|
int hdd_format_sectors(hdd_file_t *hdd, int offset, int nr_sectors) {
|
||||||
if (hdd->img_type == HDD_IMG_VHD) {
|
if (hdd->img_type == HDD_IMG_VHD) {
|
||||||
return mvhd_format_sectors((MVHDMeta *)hdd->f, offset, nr_sectors);
|
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;
|
off64_t addr;
|
||||||
int c;
|
int c;
|
||||||
uint8_t zero_buffer[512];
|
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)
|
if ((hdd->sectors - offset) < transfer_sectors)
|
||||||
transfer_sectors = hdd->sectors - offset;
|
transfer_sectors = hdd->sectors - offset;
|
||||||
addr = (uint64_t)offset * 512;
|
addr = (uint64_t)offset * 512;
|
||||||
fseeko64((FILE *)hdd->f, addr, SEEK_SET);
|
|
||||||
for (c = 0; c < transfer_sectors; c++)
|
if (hdd->img_type == HDD_IMG_RAW) {
|
||||||
fwrite(zero_buffer, 512, 1, (FILE *)hdd->f);
|
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)
|
if (nr_sectors != transfer_sectors)
|
||||||
return 1;
|
return 1;
|
||||||
|
273
src/hdd/ramdisk/ramdisk.c
Normal file
273
src/hdd/ramdisk/ramdisk.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@@ -2036,7 +2036,7 @@ static int hd_new(void *hdlg, int drive) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int hd_file(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;
|
off_t sz;
|
||||||
FILE *f = fopen64(openfilestring, "rb");
|
FILE *f = fopen64(openfilestring, "rb");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
|
Reference in New Issue
Block a user