DVX_GUI/apps/dvxbasic/stub/basstub.c

226 lines
5.8 KiB
C

// basstub.c -- DVX BASIC standalone application stub
//
// Minimal entry point for compiled BASIC applications. Reads the
// MODULE resource (serialized bytecode), FORMn resources (.frm text),
// and optional DEBUG resource from its own DXE file. Creates a VM
// and form runtime from basrt.lib and runs the program.
//
// This file is compiled into basstub.app, which is embedded as a
// resource in dvxbasic.app. "Make Executable" extracts it, renames
// it to the project name, and attaches the compiled resources.
#include "dvxApp.h"
#include "dvxDlg.h"
#include "dvxRes.h"
#include "dvxWgt.h"
#include "shellApp.h"
#include "../runtime/vm.h"
#include "../runtime/serialize.h"
#include "../formrt/formrt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
// ============================================================
// App descriptor
// ============================================================
#define STUB_STACK_SIZE 65536
AppDescriptorT appDescriptor = {
.name = "BASIC App",
.hasMainLoop = true,
.multiInstance = false,
.stackSize = STUB_STACK_SIZE,
.priority = TS_PRIORITY_NORMAL
};
// ============================================================
// Callbacks
// ============================================================
static AppContextT *sAc = NULL;
static void stubPrint(void *ctx, const char *text, bool newline) {
(void)ctx;
(void)text;
(void)newline;
}
static bool stubInput(void *ctx, const char *prompt, char *buf, int32_t bufSize) {
(void)ctx;
buf[0] = '\0';
if (!sAc) {
return false;
}
return dvxInputBox(sAc, "Input", prompt, "", buf, bufSize);
}
static bool stubDoEvents(void *ctx) {
(void)ctx;
if (!sAc) {
return false;
}
return dvxUpdate(sAc);
}
// ============================================================
// appMain
// ============================================================
int32_t appMain(DxeAppContextT *ctx) {
sAc = ctx->shellCtx;
// Open our own resources
DvxResHandleT *res = dvxResOpen(ctx->appPath);
if (!res) {
dvxMessageBox(sAc, "Error", "No compiled module found in this application.", 0);
return 1;
}
// Read app name and update the shell's app record
uint32_t nameSize = 0;
char *appName = (char *)dvxResRead(res, "name", &nameSize);
if (appName) {
ShellAppT *app = shellGetApp(ctx->appId);
if (app) {
snprintf(app->name, SHELL_APP_NAME_MAX, "%s", appName);
}
free(appName);
}
// Set help file path if present
uint32_t helpNameSize = 0;
char *helpName = (char *)dvxResRead(res, "helpfile", &helpNameSize);
if (helpName) {
snprintf(ctx->helpFile, sizeof(ctx->helpFile), "%s%c%s", ctx->appDir, DVX_PATH_SEP, helpName);
free(helpName);
}
// Load MODULE resource
uint32_t modSize = 0;
uint8_t *modData = (uint8_t *)dvxResRead(res, "MODULE", &modSize);
if (!modData) {
dvxMessageBox(sAc, "Error", "MODULE resource not found.", 0);
dvxResClose(res);
return 1;
}
BasModuleT *mod = basModuleDeserialize(modData, (int32_t)modSize);
free(modData);
if (!mod) {
dvxMessageBox(sAc, "Error", "Failed to deserialize module.", 0);
dvxResClose(res);
return 1;
}
// Load optional DEBUG resource
uint32_t dbgSize = 0;
uint8_t *dbgData = (uint8_t *)dvxResRead(res, "DEBUG", &dbgSize);
if (dbgData) {
basDebugDeserialize(mod, dbgData, (int32_t)dbgSize);
free(dbgData);
}
// Create VM
BasVmT *vm = basVmCreate();
basVmLoadModule(vm, mod);
basVmSetPrintCallback(vm, stubPrint, NULL);
basVmSetInputCallback(vm, stubInput, NULL);
basVmSetDoEventsCallback(vm, stubDoEvents, NULL);
// Set app paths
snprintf(vm->appPath, DVX_MAX_PATH, "%s", ctx->appDir);
snprintf(vm->appConfig, DVX_MAX_PATH, "%s", ctx->appDir);
snprintf(vm->appData, DVX_MAX_PATH, "%s", ctx->appDir);
// Set extern call callbacks (required for DECLARE LIBRARY functions)
BasExternCallbacksT extCb;
memset(&extCb, 0, sizeof(extCb));
extCb.resolveExtern = basExternResolve;
extCb.callExtern = basExternCall;
basVmSetExternCallbacks(vm, &extCb);
// Create form runtime
BasFormRtT *rt = basFormRtCreate(sAc, vm, mod);
// Register .frm source text for lazy loading
for (int32_t i = 0; i < 256; i++) {
char resName[16];
snprintf(resName, sizeof(resName), "FORM%ld", (long)i);
uint32_t frmSize = 0;
char *frmText = (char *)dvxResRead(res, resName, &frmSize);
if (!frmText) {
break;
}
// Extract form name from "Begin Form <name>" line
char frmName[BAS_MAX_CTRL_NAME] = "";
const char *p = frmText;
while (*p) {
while (*p == ' ' || *p == '\t') { p++; }
if (strncasecmp(p, "Begin Form ", 11) == 0) {
p += 11;
while (*p == ' ' || *p == '\t') { p++; }
int32_t ni = 0;
while (*p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && ni < BAS_MAX_CTRL_NAME - 1) {
frmName[ni++] = *p++;
}
frmName[ni] = '\0';
break;
}
while (*p && *p != '\n') { p++; }
if (*p == '\n') { p++; }
}
if (frmName[0]) {
basFormRtRegisterFrm(rt, frmName, frmText, (int32_t)frmSize);
}
free(frmText);
}
dvxResClose(res);
// Load all cached forms and show the startup form
basFormRtLoadAllForms(rt, NULL);
// Run bytecode + VB-style event loop
basFormRtRunSimple(rt);
// Cleanup
basFormRtDestroy(rt);
basVmDestroy(vm);
basModuleFree(mod);
return 0;
}