cyberslak
/lightsout
/amendments
/2
Preference support
Adds support for (autosaved) preferences, containing the server configuration and currently registered entities.
cyberslak made amendment 2 about 1 month ago
--- preferences.c Sun Mar 9 22:43:30 2025
+++ preferences.c Sun Mar 9 22:43:30 2025
@@ -0,0 +1,199 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include "preferences.h"
+#include "util.h"
+#include "dbuf.h"
+#include "entity.h"
+
+extern PrefHandle gPreferences;
+
+static short preferences_get_fsspec(FSSpec* out)
+{
+ short ret = 0;
+ char pathBuf[256];
+ short len;
+ OSErr err;
+
+ getcwd(pathBuf, 256);
+ c2pstr(pathBuf);
+
+ len = pathBuf[0];
+ len += strlcpy(pathBuf + len + 1, kPreferencesFile, 255 - len);
+ if (len > 255)
+ die("preferences path too long!");
+
+ pathBuf[0] = len;
+ err = FSMakeFSSpec(0, 0, (const u8*)pathBuf, out);
+
+ if (!(err == noErr || err == -43 /* file not found */))
+ die("Error during FSMakeFSSpec: %hd", err);
+
+ return 0;
+}
+
+/* Generate a new, empty preferences resource. */
+static Handle preferences_new()
+{
+ PrefHandle hnd = (PrefHandle)NewHandle(sizeof(**hnd));
+ if (!hnd) die("out of memory");
+ HLock((Handle)hnd);
+
+ (**gPreferences).server_name[0] = 0;
+ (**gPreferences).token[0] = 0;
+ (**gPreferences).port = 0;
+
+ HUnlock((Handle)hnd);
+ return (Handle)hnd;
+}
+
+PrefHandle preferences_load()
+{
+ Handle prefHandle;
+ FSSpec spec;
+ OSErr err;
+ short refNum;
+
+ if (preferences_get_fsspec(&spec) < 0)
+ return NULL;
+
+open:
+ refNum = FSpOpenResFile(&spec, fsRdWrPerm);
+
+ if (refNum < 0)
+ {
+ // attempt to create a new resource file
+ FSpCreateResFile(&spec, 'LitO', 'pref', smRoman);
+ err = ResError();
+ if (err != 0)
+ die("error while creating resource file: %d", err);
+ goto open;
+ }
+
+ prefHandle = Get1Resource('PREF', kPrefsId);
+
+ if (!prefHandle)
+ {
+ prefHandle = preferences_new();
+ AddResource(prefHandle, 'PREF', kPrefsId, "\pPreferences");
+ UpdateResFile(refNum);
+ // todo error handling here
+ }
+
+ return (PrefHandle)prefHandle;
+}
+
+short preferences_save(PrefHandle prefs)
+{
+ short refNum = CurResFile();
+ ChangedResource((Handle)prefs);
+ UpdateResFile(refNum);
+}
+
+short preferences_dialog()
+{
+ DialogPtr dlog;
+ GrafPtr oldPort;
+ bool dialogDone = false;
+ short itemHit;
+ Handle addrHnd, portHnd, tokHnd, save, cancel;
+ short addrType, portType, tokType, saveType, cancelType;
+ short ret = 0;
+ unsigned char portBuf[256];
+
+ dlog = GetNewDialog(kPreferencesDialogId, nil, (WindowPtr)-1);
+
+ GetDialogItem(dlog, 1, &saveType, &save, nil);
+ GetDialogItem(dlog, 2, &cancelType, &cancel, nil);
+ GetDialogItem(dlog, 5, &addrType, &addrHnd, nil);
+ GetDialogItem(dlog, 7, &portType, &portHnd, nil);
+ GetDialogItem(dlog, 9, &tokType, &tokHnd, nil);
+
+ SetDialogDefaultItem(dlog, 1);
+ SetDialogCancelItem(dlog, 2);
+ SetDialogTracksCursor(dlog, true);
+
+ ShowWindow(dlog);
+ GetPort(&oldPort);
+ SetPort(dlog);
+
+ while (!dialogDone)
+ {
+ ModalDialog(nil, &itemHit);
+ switch (itemHit)
+ {
+ case 1:
+ dialogDone = true;
+ break;
+ case 2: // cancel
+ ret = -1;
+ goto fail;
+ break;
+ }
+ }
+
+ HLock((Handle)gPreferences);
+
+ GetDialogItemText(addrHnd, (u8*)(**gPreferences).server_name);
+ GetDialogItemText(tokHnd, (u8*)(**gPreferences).token);
+ GetDialogItemText(portHnd, portBuf);
+ p2cstr((u8*)(**gPreferences).server_name);
+ p2cstr((u8*)(**gPreferences).token);
+
+ if (sscanf(p2cstr(portBuf), "%hu", &(**gPreferences).port) != 1)
+ {
+ ret = -1;
+ }
+
+ HUnlock((Handle)gPreferences);
+
+fail:
+ DisposeDialog(dlog);
+
+ SetPort(oldPort);
+ return ret;
+}
+
+short
+preferences_save_entities(list_t *entities)
+{
+ DB_INIT(entBuf, 1024);
+ list_t *node;
+ struct entity *ent;
+ Handle entHandle, curHandle;
+ short refNum = CurResFile();
+
+ list_foreach(entities, node)
+ {
+ ent = container_of(node, struct entity, node);
+ db_printf(&entBuf, "%s", ent->id);
+ entBuf.len += 1; // skip null terminator
+ }
+
+ entHandle = NewHandle(entBuf.len);
+ if (!entHandle)
+ die("Unable to save currently active entities");
+
+ HLock(entHandle);
+ memcpy(*entHandle, entBuf.buf, entBuf.len);
+ HUnlock(entHandle);
+
+ db_destroy(&entBuf);
+
+ SetResLoad(false);
+
+ if ((curHandle = GetResource('ENTR', kEntsId)) != nil)
+ RemoveResource(curHandle);
+
+ SetResLoad(true);
+ AddResource(entHandle, 'ENTR', kEntsId, "\pSaved entries");
+
+ UpdateResFile(refNum);
+}
+
+Handle preferences_get_entities()
+{
+ Handle hnd = GetResource('ENTR', kEntsId);
+ return hnd;
+}
--- preferences.h Sun Mar 9 22:59:40 2025
+++ preferences.h Sun Mar 9 22:59:40 2025
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "list.h"
+
+typedef struct lo_preferences
+{
+ char server_name[256];
+ short port;
+ char token[256];
+} **PrefHandle;
+
+#define kPreferencesDialogId 129
+#define kPreferencesFile ((const char*)"Lights Out Preferences")
+#define kPrefsId 1000
+#define kEntsId 1000
+
+PrefHandle preferences_load();
+short preferences_save(PrefHandle prefs);
+
+short preferences_dialog();
+
+short preferences_save_entities(list_t *entities);
+Handle preferences_get_entities();