From 020b1d77063c8101c5389520e22f0823a0ce8a67 Mon Sep 17 00:00:00 2001 From: kdedon <56561055+kdedon@users.noreply.github.com> Date: Mon, 13 Jan 2025 07:35:06 -0600 Subject: [PATCH] Read new .cfg file, update drive structure (#949) --- ide.cpp | 9 +- support/vhd/vhdcfg.h | 15 +++ support/vhd/vhdcgf.cpp | 246 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 support/vhd/vhdcfg.h create mode 100644 support/vhd/vhdcgf.cpp diff --git a/ide.cpp b/ide.cpp index 4b342a1..197c72b 100644 --- a/ide.cpp +++ b/ide.cpp @@ -13,6 +13,7 @@ #include #include "support/x86/x86.h" +#include "support/vhd/vhdcfg.h" #include "support/minimig/minimig_hdd.h" #include "support/minimig/minimig_config.h" #include "spi.h" @@ -106,6 +107,7 @@ int ide_img_mount(fileTYPE *f, const char *name, int rw) writable = rw && FileCanWrite(name); ret = FileOpenEx(f, name, writable ? (O_RDWR | O_SYNC) : O_RDONLY); if (!ret) printf("Failed to open file %s\n", name); + else strcpy(f->path, name); } } @@ -467,7 +469,12 @@ void ide_img_set(uint32_t drvnum, fileTYPE *f, int cd, int sectors, int heads, i { if (drive->present) { - ide_set_geometry(drive, sectors, heads); + if (!drive->chd_f) + { + if (parse_vhd_config(drive)) ide_set_geometry(drive, sectors, heads); + else ide_set_geometry(drive, drive->spt, drive->heads); + } + else ide_set_geometry(drive, sectors, heads); if (offset && drive->cylinders < 65535) drive->cylinders++; drive->offset = offset; drive->type = type; diff --git a/support/vhd/vhdcfg.h b/support/vhd/vhdcfg.h new file mode 100644 index 0000000..608b73f --- /dev/null +++ b/support/vhd/vhdcfg.h @@ -0,0 +1,15 @@ +#ifndef VHD_CFG_H +#define VHD_CFG_H + +#include + +#include "ide.h" + +typedef enum +{ + VHD_NOERROR = 0, VHD_NO_CONFIG, VHD_INVALID_CONFIG, VHD_INVALID_SIZE, VHD_MISSING_CONFIG +} vhd_error; + +vhd_error parse_vhd_config(drive_t *drive); + +#endif // VHD_CFG_H diff --git a/support/vhd/vhdcgf.cpp b/support/vhd/vhdcgf.cpp new file mode 100644 index 0000000..e8fa31b --- /dev/null +++ b/support/vhd/vhdcgf.cpp @@ -0,0 +1,246 @@ +// vhdcfg.cpp uses simplified code from cfg.cpp +// 2015, rok.krajnc@gmail.com +// 2017+, Sorgelig + +#include +#include +#include +#include + +#include "vhdcfg.h" +#include "file_io.h" + +/* ATA Spec: + Sectors: 8-bit + Heads: 4-bit + Cylinders: 16-bit +*/ + +typedef enum +{ + VHD_UINT8 = 0, VHD_UINT16 +} vhd_cfg_type_t; + +typedef struct +{ + const char* name; + void* var; + vhd_cfg_type_t type; + uint16_t min; + uint16_t max; +} vhd_var_t; + +#define CFG_LINE_SIZE 64 +#define CHAR_IS_LINEEND(c) (((c) == '\n')) + +fileTYPE cfg_file; + +int cfg_pt = 0; +static char cfg_getch() +{ + static uint8_t buf[512]; + if (!(cfg_pt & 0x1ff)) FileReadSec(&cfg_file, buf); + if (cfg_pt >= cfg_file.size) return 0; + return buf[(cfg_pt++) & 0x1ff]; +} + +static int cfg_getline(char* line) +{ + char c, ignore = 0, skip = 1; + int i = 0; + + while ((c = cfg_getch())) + { + if (!std::isspace(c)) skip = 0; + if (i >= (CFG_LINE_SIZE - 1)) ignore = 1; + + if (CHAR_IS_LINEEND(c)) break; + if ((std::isspace(c) || std::isalnum(c)) && !ignore && !skip) line[i++] = c; + } + line[i] = 0; + while (i > 0 && std::isspace(line[i - 1])) line[--i] = 0; + return c == 0; +} + +static vhd_error ini_parse_numeric(const vhd_var_t *var, const char *text, void *out) +{ + uint32_t u32 = 0; + char *endptr = nullptr; + + bool out_of_range = true; + bool invalid_format = false; + + u32 = strtoul(text, &endptr, 0); + if (u32 < var->min) u32 = var->min; + else if (u32 > var->max) u32 = var->max; + else out_of_range = false; + + if (*endptr) + { + printf("VHD CFG ERROR: %s: \'%s\' not a number\n", var->name, text); + return VHD_INVALID_CONFIG; + } + else if (out_of_range) + { + printf("VHD CFG ERROR: %s: \'%s\' out of range\n", var->name, text); + return VHD_INVALID_CONFIG; + } + else if (invalid_format) + { + printf("VHD CFG ERROR: %s: \'%s\' invalid format\n", var->name, text); + return VHD_INVALID_CONFIG; + } + + switch (var->type) + { + case VHD_UINT8: *(uint8_t*)out = u32; break; + case VHD_UINT16: *(uint16_t*)out = u32; break; + default: break; + } + return VHD_NOERROR; +} + +static vhd_error ini_parse_var(char* buf, vhd_var_t vars[], size_t var_count) +{ + // find var + int i = 0; + while (1) + { + if (buf[i] == '=' || std::isspace(buf[i])) + { + buf[i] = 0; + break; + } + else if (!buf[i]) return VHD_NOERROR; + i++; + } + + // parse var + int var_id = -1; + for (size_t j = 0; j < var_count; j++) + { + if (!strcasecmp(buf, vars[j].name)) var_id = j; + } + + if (var_id == -1) + { + printf("VHD CFG ERROR: %s: unknown option\n", buf); + return VHD_INVALID_CONFIG; + } + else // get data + { + i++; + while (std::isspace(buf[i])) i++; + + const vhd_var_t *var = &vars[var_id]; + return ini_parse_numeric(var, &buf[i], var->var); + } +} + +static vhd_error cfg_parse(const char *name, vhd_var_t vars[], size_t var_count) +{ + char line[CFG_LINE_SIZE]; + int eof; + vhd_error res = VHD_NOERROR; + + memset(line, 0, sizeof(line)); + memset(&cfg_file, 0, sizeof(cfg_file)); + + if (!FileOpen(&cfg_file, name)) return VHD_NO_CONFIG; + + cfg_pt = 0; + + // parse ini + while (1) + { + // get line + eof = cfg_getline(line); + res = ini_parse_var(line, vars, var_count); + if (res != VHD_NOERROR) + { + return res; + } + + // if end of file, stop + if (eof) break; + } + + FileClose(&cfg_file); + + return res; +} + +// make sure the values make sense +vhd_error validate(drive_t *drive, uint8_t sectors, uint8_t heads, uint16_t cylinders) +{ + // Both sectors and heads must be defined + if (!sectors || !heads) return VHD_MISSING_CONFIG; + + __off64_t config_size = heads * sectors * 512; + if (cylinders == 0 && drive->f->size < config_size) + { + return VHD_INVALID_SIZE; + } + else if (cylinders == 0) return VHD_NOERROR; + + config_size = config_size * cylinders; + if (drive->f->size < config_size) return VHD_INVALID_SIZE; + + if (drive->f->size != config_size) + { + printf("config override: new size %lli\n", config_size); + drive->total_sectors = config_size / 512; + } + return VHD_NOERROR; +} + +// parse_vhd_config will take a filename and replace the extension with `.cfg` then attempt to parse +// CHS values into a provided `vhd_config_t`. +vhd_error parse_vhd_config(drive_t *drive) +{ + const char *name = drive->f->path; + struct + { + uint8_t sectors; + uint8_t heads; + uint16_t cylinders; + } parsed_values; + + // vhd_var_t defines the possible values that can be specified for a given image. + // If this file exists, both `sectors` and `heads` must be defined for it to parse + // successfully. Cylinders may also be specified if the entire file shouldn't be + // used, i.e. -- avoid overwriting a footer. + vhd_var_t vhd_vars[] = + { + { "SECTORS", (void*)(&(parsed_values.sectors)), VHD_UINT8, 17, 256 }, + { "HEADS", (void*)(&(parsed_values.heads)), VHD_UINT8, 2, 16 }, + { "CYLINDERS", (void*)(&(parsed_values.cylinders)), VHD_UINT16, 0, 65535 } + }; + + char cfg_name[256]; + + const char *ext = strrchr(name, '.'); + if (!ext || ext == name || ext == name + strlen(name) - 1) + { + return VHD_NO_CONFIG; + } + + // find the length of the name before the extension + size_t name_len = ext - name; + strncpy(cfg_name, name, name_len); + strncpy(cfg_name+name_len, ".cfg", 4); + + vhd_error result = cfg_parse(cfg_name, vhd_vars, sizeof(vhd_vars)/sizeof(vhd_var_t)); + if (result == VHD_NOERROR) + { + result = validate(drive, parsed_values.sectors, parsed_values.heads, parsed_values.cylinders); + if (result == VHD_NOERROR) + { + printf("config override: cylinders %d heads %d sectors %d\n", parsed_values.cylinders, parsed_values.heads, parsed_values.sectors); + drive->cylinders=parsed_values.cylinders; + drive->heads=parsed_values.heads; + drive->spt=parsed_values.sectors; + } + } + return result; +}