DVX_GUI/src/apps/kpunch/dvxbasic/basBuild.c

152 lines
5.9 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.
// basBuild.c -- shared "emit .app resource set" step
//
// See basBuild.h for the public contract. This file used to live inline
// in two places: ideMain.c (the IDE's Make Executable path) and
// bascomp.c (the standalone command-line compiler). Both implementations
// were essentially identical, so they are now consolidated here.
//
// The canonical emit order is:
// 1. BAS_RES_NAME (always, falls back to "BASIC App")
// 2. BAS_RES_AUTHOR / PUBLISHER / VERSION / COPYRIGHT / DESCRIPTION
// (each skipped if empty)
// 3. BAS_RES_ICON32 (file via iconPath, else iconData bytes)
// 4. BAS_RES_HELPFILE (filename only, if helpFile set)
// 5. BAS_RES_MODULE (bytecode)
// 6. BAS_RES_DEBUG (optional)
// 7. FORM0, FORM1, ... (one per spec->formCount)
#include "basBuild.h"
#include "basRes.h"
#include "../../../libs/kpunch/libdvx/dvxRes.h"
#include "../../../libs/kpunch/libdvx/platform/dvxPlat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ------------------------------------------------------------
// Internal helpers
// ------------------------------------------------------------
static int32_t appendText(const char *path, const char *name, const char *value) {
if (!value || !value[0]) {
return 0;
}
return dvxResAppend(path, name, DVX_RES_TEXT, value, (uint32_t)strlen(value) + 1);
}
static int32_t emitIcon(const char *path, const BasBuildSpecT *spec) {
// If a disk path was given, load it and embed. Otherwise use the
// pre-loaded bytes (used by the IDE for its "noicon" fallback).
if (spec->iconPath && spec->iconPath[0]) {
int32_t iconLen = 0;
char *iconData = platformReadFile(spec->iconPath, &iconLen);
int32_t rc = 0;
if (iconData) {
rc = dvxResAppend(path, BAS_RES_ICON32, DVX_RES_ICON, iconData, (uint32_t)iconLen);
free(iconData);
}
return rc;
}
if (spec->iconData && spec->iconSize > 0) {
return dvxResAppend(path, BAS_RES_ICON32, DVX_RES_ICON, spec->iconData, (uint32_t)spec->iconSize);
}
return 0;
}
// ------------------------------------------------------------
// Public API
// ------------------------------------------------------------
int32_t basBuildEmitResources(const char *outPath, const BasBuildSpecT *spec) {
if (!outPath || !spec) {
return -1;
}
// Accumulate every append/write result so a disk-full or I/O failure on
// any resource is reported instead of being silently treated as success.
int32_t rc = 0;
// Project metadata. Name is required -- fall back to a generic label
// so the compiled app always has something sensible to display.
const char *projName = (spec->projName && spec->projName[0]) ? spec->projName : "BASIC App";
rc |= appendText(outPath, BAS_RES_NAME, projName);
rc |= appendText(outPath, BAS_RES_AUTHOR, spec->author);
rc |= appendText(outPath, BAS_RES_PUBLISHER, spec->publisher);
rc |= appendText(outPath, BAS_RES_VERSION, spec->version);
rc |= appendText(outPath, BAS_RES_COPYRIGHT, spec->copyright);
rc |= appendText(outPath, BAS_RES_DESCRIPTION, spec->description);
// Icon.
rc |= emitIcon(outPath, spec);
// Help file name (just the basename -- stub resolves it next to the app).
if (spec->helpFile && spec->helpFile[0]) {
const char *helpBase = platformPathBaseName(spec->helpFile);
rc |= appendText(outPath, BAS_RES_HELPFILE, helpBase);
}
// Bytecode module.
if (spec->moduleData && spec->moduleLen > 0) {
rc |= dvxResAppend(outPath, BAS_RES_MODULE, DVX_RES_BINARY, spec->moduleData, (uint32_t)spec->moduleLen);
}
// Optional debug info.
if (spec->debugData && spec->debugLen > 0) {
rc |= dvxResAppend(outPath, BAS_RES_DEBUG, DVX_RES_BINARY, spec->debugData, (uint32_t)spec->debugLen);
}
// Form resources. Callers pre-strip / pre-obfuscate as needed; we just
// write them out as FORM0, FORM1, ... Number the OUTPUT densely with
// outIdx rather than the source index i: skipping an empty/NULL form
// by the source index would leave a numbering gap (FORM0, FORM2, ...)
// and the stub's reader stops at the first missing index, silently
// dropping every later form.
int32_t outIdx = 0;
for (int32_t i = 0; i < spec->formCount; i++) {
if (!spec->formData || !spec->formData[i] || !spec->formLens || spec->formLens[i] <= 0) {
continue;
}
char resName[16];
snprintf(resName, sizeof(resName), BAS_RES_FORM_FMT, (long)outIdx);
rc |= dvxResAppend(outPath, resName, DVX_RES_BINARY, spec->formData[i], (uint32_t)spec->formLens[i]);
outIdx++;
}
return rc;
}