278 lines
6.5 KiB
C
278 lines
6.5 KiB
C
// 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
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;
|
|
}
|