152 lines
5.9 KiB
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;
|
|
}
|