// 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 #include #include // ------------------------------------------------------------ // 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; }