From 043e2b6baa6d8c2d1abecbd9006a38d4c463b889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Hrdli=C4=8Dka?= <13226155+dhrdlicka@users.noreply.github.com> Date: Mon, 1 Sep 2025 18:35:56 +0200 Subject: [PATCH] Rewrite custom EDID loading --- src/include/86box/vid_ddc.h | 7 +- src/qt/qt_settingsdisplay.cpp | 7 +- src/utils/CMakeLists.txt | 1 - src/utils/edid_parse.cpp | 119 ------------------------ src/video/CMakeLists.txt | 1 + src/video/vid_ddc.c | 168 +++++++++++++--------------------- src/video/vid_ddc_custom.c | 110 ++++++++++++++++++++++ 7 files changed, 182 insertions(+), 231 deletions(-) delete mode 100644 src/utils/edid_parse.cpp create mode 100644 src/video/vid_ddc_custom.c diff --git a/src/include/86box/vid_ddc.h b/src/include/86box/vid_ddc.h index 8d68d0adf..32c8e88c5 100644 --- a/src/include/86box/vid_ddc.h +++ b/src/include/86box/vid_ddc.h @@ -20,8 +20,9 @@ #ifndef EMU_VID_DDC_H #define EMU_VID_DDC_H -extern void *ddc_init(void *i2c); -extern void ddc_close(void *eeprom); -extern void *ddc_create_default_edid(ssize_t* size_out); +extern void *ddc_init(void *i2c); +extern void ddc_close(void *eeprom); +extern size_t ddc_create_default_edid(uint8_t **size_out); +extern size_t ddc_load_edid(char *path, uint8_t *buf, size_t size); #endif /*EMU_VID_DDC_H*/ diff --git a/src/qt/qt_settingsdisplay.cpp b/src/qt/qt_settingsdisplay.cpp index bbe64dc00..ed9ba220c 100644 --- a/src/qt/qt_settingsdisplay.cpp +++ b/src/qt/qt_settingsdisplay.cpp @@ -23,6 +23,8 @@ #include #include +#include + extern "C" { #include <86box/86box.h> #include <86box/device.h> @@ -343,11 +345,10 @@ void SettingsDisplay::on_pushButtonExportDefault_clicked() if (!str.isEmpty()) { QFile file(str); if (file.open(QFile::WriteOnly)) { - ssize_t size = 0; - auto bytes = ddc_create_default_edid(&size); + uint8_t *bytes = nullptr; + auto size = ddc_create_default_edid(&bytes); file.write((char*)bytes, size); file.close(); } } } - diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index e4dea5d61..bcbc7aafd 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -19,7 +19,6 @@ add_library(utils OBJECT cJSON.c crc.c crc32.c - edid_parse.cpp fifo.c fifo8.c ini.c diff --git a/src/utils/edid_parse.cpp b/src/utils/edid_parse.cpp deleted file mode 100644 index 5aff4e3e6..000000000 --- a/src/utils/edid_parse.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -extern "C" -{ -#include <86box/86box.h> -#include <86box/plat.h> - -extern int ini_detect_bom(const char *fn); -extern ssize_t local_getline(char **buf, size_t *bufsiz, FILE *fp); -} - -// https://stackoverflow.com/a/64886763 -static std::vector split(const std::string str, const std::string regex_str) -{ - std::regex regexz(regex_str); - std::vector list(std::sregex_token_iterator(str.begin(), str.end(), regexz, -1), - std::sregex_token_iterator()); - return list; -} - -extern "C" -{ - bool parse_edid_decode_file(const char* path, uint8_t* out, ssize_t* size_out) - { - std::regex regexLib("^([a-f0-9]{32}|[a-f0-9 ]{47})$", std::regex_constants::egrep); - FILE* file = NULL; - try { - bool bom = ini_detect_bom(path); - { - // First check for "edid-decode (hex)" string. - file = plat_fopen(path, "rb"); - if (file) { - std::string str; - ssize_t size; - if (!fseek(file, 0, SEEK_END)) { - size = ftell(file); - if (size != -1) { - str.resize(size); - } - fseek(file, 0, SEEK_SET); - auto read = fread((void*)str.data(), 1, size, file); - str.resize(read); - fclose(file); - file = NULL; - - if (str.size() == 0) { - return false; - } - - if (str.find("edid-decode (hex):") == std::string::npos) { - return false; - } - } - } else { - return false; - } - } - file = plat_fopen(path, "rb"); - if (file) { - size_t size = 0; - std::string edid_decode_text; - fseek(file, 0, SEEK_END); - size = ftell(file); - fseek(file, 0, SEEK_SET); - if (bom) { - fseek(file, 3, SEEK_SET); - size -= 3; - } - edid_decode_text.resize(size); - auto err = fread((void*)edid_decode_text.data(), size, 1, file); - fclose(file); - file = NULL; - if (err == 0) { - return false; - } - std::istringstream isstream(edid_decode_text); - std::string line; - std::string edid; - while (std::getline(isstream, line)) { - if (line[line.size() - 1] == '\r') { - line.resize(line.size() - 1); - } - std::smatch matched; - if (std::regex_match(line, matched, regexLib)) { - edid.append(matched.str() + " "); - } - } - if (edid.size() >= 3) { - edid.resize(edid.size() - 1); - auto vals = split(edid, "\\s+"); - if (vals.size()) { - *size_out = vals.size(); - if (vals.size() > 256) - return false; - for (size_t i = 0; i < vals.size(); i++) { - out[i] = (uint8_t)std::strtoul(&vals[i][0], nullptr, 16); - } - return true; - } - } - } - - return false; - } catch (std::bad_alloc&) { - if (file) { - fclose(file); - file = NULL; - } - return false; - } - return false; - } -} \ No newline at end of file diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 6301956c5..4667a3b4f 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -44,6 +44,7 @@ add_library(vid OBJECT # DDC / monitor identification stuff vid_ddc.c + vid_ddc_custom.c # CARDS start here diff --git a/src/video/vid_ddc.c b/src/video/vid_ddc.c index 6d86a25c9..aeaba3965 100644 --- a/src/video/vid_ddc.c +++ b/src/video/vid_ddc.c @@ -14,7 +14,6 @@ * * Copyright 2020 RichardG. */ -#include #include #include #include @@ -130,8 +129,8 @@ typedef struct { uint8_t padding[15], checksum2; } edid_t; -void* -ddc_create_default_edid(ssize_t* size_out) +size_t +ddc_create_default_edid(uint8_t **out) { edid_t *edid = malloc(sizeof(edid_t)); memset(edid, 0, sizeof(edid_t)); @@ -226,120 +225,79 @@ ddc_create_default_edid(ssize_t* size_out) edid->checksum2 += edid_bytes[c]; edid->checksum2 = 256 - edid->checksum2; - if (size_out) - *size_out = sizeof(edid_t); - - return edid; + if (out) { + *out = edid_bytes; + } + + return sizeof(edid_t); } -extern bool parse_edid_decode_file(const char* path, uint8_t* out, ssize_t* size); +void * +ddc_init_with_custom_edid(char *edid_path, void *i2c) +{ + uint8_t buffer[384] = { 0 }; + size_t size = ddc_load_edid(edid_path, buffer, sizeof(buffer)); + + if (size > 256) { + wchar_t errmsg[2048] = { 0 }; + wchar_t path[2048] = { 0 }; + +#ifdef _WIN32 + mbstoc16s(path, monitor_edid_path, sizeof_w(path)); +#else + mbstowcs(path, monitor_edid_path, sizeof_w(path)); +#endif + swprintf(errmsg, sizeof_w(errmsg), plat_get_string(STRING_EDID_TOO_LARGE), path); + ui_msgbox_header(MBX_ERROR, L"EDID", errmsg); + + return NULL; + } else if (size == 0) { + return NULL; + } else if (size < 128) { + size = 128; + } else if (size < 256) { + size = 256; + } + + int checksum = 0; + for (int i = 0; i < 127; i++) { + checksum += buffer[i]; + } + + buffer[127] = 256 - checksum; + + if (size == 256) { + checksum = 0; + + for (int i = 128; i < 255; i++) { + checksum += buffer[i]; + } + buffer[255] = 256 - checksum; + } + + uint8_t *edid_bytes = malloc(size); + memcpy(edid_bytes, buffer, size); + + return i2c_eeprom_init(i2c, 0x50, edid_bytes, size); +} void * ddc_init(void *i2c) { - ssize_t edid_size = 0; - uint8_t* edid_bytes = NULL; - if (monitor_edid == 1 && monitor_edid_path[0]) { - FILE* file; - { - edid_bytes = calloc(1, 256); - if (parse_edid_decode_file(monitor_edid_path, edid_bytes, &edid_size) == false) { - if (edid_size > 256) { - wchar_t errmsg[2048] = { 0 }; - wchar_t path[2048] = { 0 }; + if (monitor_edid && monitor_edid_path[0]) { + void *ret = ddc_init_with_custom_edid(monitor_edid_path, i2c); -#ifdef _WIN32 - mbstoc16s(path, monitor_edid_path, sizeof_w(path)); -#else - mbstowcs(path, monitor_edid_path, sizeof_w(path)); -#endif - swprintf(errmsg, sizeof_w(errmsg), plat_get_string(STRING_EDID_TOO_LARGE), path); - ui_msgbox_header(MBX_ERROR, L"EDID", errmsg); - } - free(edid_bytes); - } else { - goto calculate_cksum; - } + if (ret) { + return ret; } - file = plat_fopen(monitor_edid_path, "rb"); - - if (!file) - goto default_init; - - if (fseek(file, 0, SEEK_END) == -1) { - fclose(file); - goto default_init; - } - - edid_size = ftell(file); - fseek(file, 0, SEEK_SET); - - if (edid_size <= 0) { - fclose(file); - goto default_init; - } - - if (edid_size > 256) { - wchar_t errmsg[2048] = { 0 }; - wchar_t path[2048] = { 0 }; - -#ifdef _WIN32 - mbstoc16s(path, monitor_edid_path, sizeof_w(path)); -#else - mbstowcs(path, monitor_edid_path, sizeof_w(path)); -#endif - swprintf(errmsg, sizeof_w(errmsg), plat_get_string(STRING_EDID_TOO_LARGE), path); - ui_msgbox_header(MBX_ERROR, L"EDID", errmsg); - fclose(file); - goto default_init; - } - - edid_bytes = calloc(1, edid_size); - if (!edid_bytes) { - fclose(file); - goto default_init; - } - - if (fread(edid_bytes, edid_size, 1, file) <= 0) { - free(edid_bytes); - fclose(file); - goto default_init; - } - - fclose(file); -calculate_cksum: - if (edid_size < 128) { - edid_bytes = realloc(edid_bytes, 128); - edid_size = 128; - } else if (edid_size < 256) { - edid_bytes = realloc(edid_bytes, 256); - edid_size = 256; - } - - { - int checksum = 0; - for (uint8_t c = 0; c < 127; c++) - checksum += edid_bytes[c]; - edid_bytes[127] = 256 - checksum; - - if (edid_size == 256) { - checksum = 0; - - for (uint8_t c = 128; c < 255; c++) { - checksum += edid_bytes[c]; - } - edid_bytes[255] = 256 - checksum; - } - } - - return i2c_eeprom_init(i2c, 0x50, edid_bytes, edid_size, 0); } -default_init: - edid_size = sizeof(edid_t); - edid_bytes = ddc_create_default_edid(&edid_size); + + uint8_t *edid_bytes; + size_t edid_size = ddc_generate_default_edid(&edid_bytes); return i2c_eeprom_init(i2c, 0x50, edid_bytes, edid_size, 0); } + void ddc_close(void *eeprom) { diff --git a/src/video/vid_ddc_custom.c b/src/video/vid_ddc_custom.c new file mode 100644 index 000000000..4b8e300e5 --- /dev/null +++ b/src/video/vid_ddc_custom.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include + +#define EDID_BLOCK_SIZE 128 +#define EDID_HEADER 0x00FFFFFFFFFFFF00 +#define EDID_DECODE_HEADER "edid-decode (hex):" + +static size_t +read_block(FILE *fp, uint8_t *buf) +{ + uint8_t temp[64]; + size_t read = 0; + + for (int i = 0; i < 8; i++) { + if (!fgets(temp, sizeof(temp), fp)) { + return 0; + } + + char *tok = strtok(temp, " \t\r\n"); + + for (int j = 0; j < 16; j++) { + if (!tok) { + return 0; + } + + buf[read++] = strtoul(tok, NULL, 16); + tok = strtok(NULL, " \t\r\n"); + } + } + + return read; +} + +size_t +ddc_load_edid(char *path, uint8_t *buf, size_t size) +{ + FILE *fp = fopen(path, "rb"); + size_t offset = 0; + uint8_t temp[64]; + + if (!fp) { + return 0; + } + + // Check the beginning of the file for the EDID header. + uint64_t header; + fread(&header, sizeof(header), 1, fp); + + if (header == EDID_HEADER) { + // Binary format. Read as is + fseek(fp, 0, SEEK_SET); + offset = fread(buf, 1, size, fp); + + fclose(fp); + return offset; + } + + // Reopen in text mode. + fclose(fp); + fp = fopen(path, "rt"); + + if (!fp) { + return 0; + } + + // Skip the UTF-8 BOM, if any. + if (fread(temp, 1, 3, fp) != 3) { + fclose(fp); + return 0; + }; + + if (temp[0] != 0xEF || temp[1] != 0xBB || temp[2] != 0xBF) { + fseek(fp, -3, SEEK_CUR); + } + + // Find the `edid-decode (hex):` header + do { + if (!fgets(temp, sizeof(temp), fp)) { + fclose(fp); + return 0; + } + } while (strncmp(temp, EDID_DECODE_HEADER, sizeof(EDID_DECODE_HEADER) - 1)); + + while (offset + EDID_BLOCK_SIZE <= size) { + // Skip any whitespace before the next block + do { + if (!fgets(temp, sizeof(temp), fp)) { + fclose(fp); + return offset; + } + } while (strspn(temp, " \t\r\n") == strlen(temp)); + + fseek(fp, -strlen(temp), SEEK_CUR); + + // Read the block + size_t block = read_block(fp, buf + offset); + + if (block != EDID_BLOCK_SIZE) { + break; + } + + offset += block; + } + + fclose(fp); + return offset; +}