// The MIT License (MIT) // // Copyright (C) 2026 Scott Duensing // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // dvxResource.c -- DVX resource runtime API // // Reads the resource block appended to DXE3 files. The resource // block is located by reading the footer at the end of the file. #include "dvxRes.h" #include "../../../tools/dvxResWrite.h" #include #include #include int32_t dvxResAppend(const char *path, const char *name, uint32_t type, const void *data, uint32_t dataSize) { return dvxResAppendEntry(path, name, type, data, dataSize); } void dvxResClose(DvxResHandleT *h) { if (h) { free(h->entries); free(h); } } static int dvxResEntryCmp(const void *a, const void *b) { return strcmp(((const DvxResDirEntryT *)a)->name, ((const DvxResDirEntryT *)b)->name); } const DvxResDirEntryT *dvxResFind(DvxResHandleT *h, const char *name) { if (!h || !name) { return NULL; } // Entries are sorted at open() time so bsearch gives O(log n) lookup. DvxResDirEntryT key; memset(&key, 0, sizeof(key)); strncpy(key.name, name, sizeof(key.name) - 1); key.name[sizeof(key.name) - 1] = '\0'; return (const DvxResDirEntryT *)bsearch(&key, h->entries, h->entryCount, sizeof(DvxResDirEntryT), dvxResEntryCmp); } DvxResHandleT *dvxResOpen(const char *path) { if (!path) { return NULL; } FILE *f = fopen(path, "rb"); if (!f) { return NULL; } // Read footer from end of file if (fseek(f, -(int32_t)sizeof(DvxResFooterT), SEEK_END) != 0) { fclose(f); return NULL; } DvxResFooterT footer; if (fread(&footer, sizeof(footer), 1, f) != 1) { fclose(f); return NULL; } if (footer.magic != DVX_RES_MAGIC || footer.entryCount == 0) { fclose(f); return NULL; } // Read directory if (fseek(f, footer.dirOffset, SEEK_SET) != 0) { fclose(f); return NULL; } DvxResDirEntryT *entries = (DvxResDirEntryT *)malloc(footer.entryCount * sizeof(DvxResDirEntryT)); if (!entries) { fclose(f); return NULL; } if (fread(entries, sizeof(DvxResDirEntryT), footer.entryCount, f) != footer.entryCount) { free(entries); fclose(f); return NULL; } fclose(f); DvxResHandleT *h = (DvxResHandleT *)malloc(sizeof(DvxResHandleT)); if (!h) { free(entries); return NULL; } strncpy(h->path, path, sizeof(h->path) - 1); h->path[sizeof(h->path) - 1] = '\0'; h->entries = entries; h->entryCount = footer.entryCount; // Sort once so dvxResFind can bsearch. qsort(h->entries, h->entryCount, sizeof(DvxResDirEntryT), dvxResEntryCmp); return h; } void *dvxResRead(DvxResHandleT *h, const char *name, uint32_t *outSize) { const DvxResDirEntryT *entry = dvxResFind(h, name); if (!entry) { return NULL; } FILE *f = fopen(h->path, "rb"); if (!f) { return NULL; } if (fseek(f, entry->offset, SEEK_SET) != 0) { fclose(f); return NULL; } void *buf = malloc(entry->size); if (!buf) { fclose(f); return NULL; } if (fread(buf, 1, entry->size, f) != entry->size) { free(buf); fclose(f); return NULL; } fclose(f); if (outSize) { *outSize = entry->size; } return buf; } int32_t dvxResRemove(const char *path, const char *name) { if (!path || !name) { return -1; } long dxeSize = dvxResDxeContentSize(path); if (dxeSize < 0) { return -1; } DvxResDirEntryT *entries = NULL; uint32_t count = 0; uint8_t **data = NULL; dvxResReadExisting(path, dxeSize, &entries, &count, &data); if (!entries || count == 0) { return -1; } bool found = false; for (uint32_t i = 0; i < count; i++) { if (strcmp(entries[i].name, name) == 0) { free(data[i]); for (uint32_t j = i; j < count - 1; j++) { entries[j] = entries[j + 1]; data[j] = data[j + 1]; } count--; found = true; break; } } if (!found) { for (uint32_t i = 0; i < count; i++) { free(data[i]); } free(data); free(entries); return -1; } int32_t result; if (count == 0) { // No resources left -- truncate to DXE content only FILE *f = fopen(path, "rb"); if (!f) { free(data); free(entries); return -1; } uint8_t *dxeBuf = (uint8_t *)malloc((size_t)dxeSize); if (!dxeBuf) { fclose(f); free(data); free(entries); return -1; } if (fread(dxeBuf, 1, (size_t)dxeSize, f) != (size_t)dxeSize) { free(dxeBuf); fclose(f); free(data); free(entries); return -1; } fclose(f); f = fopen(path, "wb"); if (f) { fwrite(dxeBuf, 1, (size_t)dxeSize, f); fclose(f); result = 0; } else { result = -1; } free(dxeBuf); } else { result = dvxResWriteBlock(path, dxeSize, entries, count, data); } for (uint32_t i = 0; i < count; i++) { free(data[i]); } free(data); free(entries); return result; }