/** * Copyright (c) 2026 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "save/savedevice.h" #include "save/savedevicelinux.h" #include "save/settings/savesettings.h" #include "save/savejson.h" #include "asset/asset.h" #include "assert/assert.h" #include "util/mkdirp.h" #include "util/string.h" errorret_t saveDeviceLinuxInit(savedevice_t *device) { assertNotNull(device, "device cannot be null"); device->state = SAVE_DEVICE_STATE_UNKNOWN; errorOk(); } errorret_t saveDeviceLinuxUpdate(savedevice_t *device) { assertNotNull(device, "device cannot be null"); assertIsMainThread("Invalid thread"); errorOk(); } void saveDeviceLinuxCheckAvailability(savedevice_t *device) { assertNotNull(device, "device cannot be null"); assertIsMainThread("Invalid thread"); assertTrue( device->state == SAVE_DEVICE_STATE_CHECKING_AVAILABILITY, "Device state incorrect?" ); // Resolve the save directory - can't actually fail (just a // stringFormat), unlike when this derived from $HOME, but this function // returns void (via saveDeviceFireCallback below) rather than // errorret_t, so there's no return-early errorChain to use here. char_t directory[SAVE_DEVICE_LINUX_PATH_MAX]; errorret_t directoryResult = saveDeviceLinuxGetDirectory( directory, sizeof(directory) ); if(errorIsNotOk(directoryResult)) errorCatch(errorPrint(directoryResult)); // Mkdirp the save directory. int mkdirpResult = mkdirp(directory, 0700); if(mkdirpResult != 0) { device->state = SAVE_DEVICE_STATE_UNAVAILABLE; device->reasonKey = "save.linux.mkdirp_failed"; return saveDeviceFireCallback(device); } // The save device is available. device->state = SAVE_DEVICE_STATE_AVAILABLE; device->reasonKey = NULL; saveDeviceFireCallback(device); } errorret_t saveDeviceLinuxGetDirectory(char_t *dest, const size_t destSize) { assertNotNull(dest, "dest cannot be null"); stringFormat( dest, destSize, "%s/%s", ASSET.baseDirectory, SAVE_DEVICE_LINUX_SAVES_SUBDIR_NAME ); errorOk(); } errorret_t saveDeviceLinuxGetSettingsPath( char_t *dest, const size_t destSize ) { assertNotNull(dest, "dest cannot be null"); char_t directory[SAVE_DEVICE_LINUX_PATH_MAX]; errorChain(saveDeviceLinuxGetDirectory(directory, sizeof(directory))); stringFormat( dest, destSize, "%s/%s", directory, SAVE_DEVICE_LINUX_SETTINGS_FILENAME ); errorOk(); } errorret_t saveDeviceLinuxSettingsWrite( savedevice_t *device, savesettings_t *settings ) { assertNotNull(device, "device cannot be null"); assertNotNull(settings, "settings cannot be null"); char_t path[SAVE_DEVICE_LINUX_PATH_MAX]; errorChain(saveDeviceLinuxGetSettingsPath(path, sizeof(path))); writeInit(); errorret_t writeResult = saveSettingsWriteJSON(settings, doc, object); if(errorIsNotOk(writeResult)) { yyjson_mut_doc_free(doc); errorChain(writeResult); } yyjson_write_err writeErr; bool_t written = yyjson_mut_write_file(path, doc, 0, NULL, &writeErr); yyjson_mut_doc_free(doc); if(!written) { errorThrow( "Failed to write save settings file: %s (%s)", path, writeErr.msg ); } errorOk(); } errorret_t saveDeviceLinuxSettingsRead( savedevice_t *device, savesettings_t *settings ) { assertNotNull(device, "device cannot be null"); assertNotNull(settings, "settings cannot be null"); char_t path[SAVE_DEVICE_LINUX_PATH_MAX]; errorChain(saveDeviceLinuxGetSettingsPath(path, sizeof(path))); yyjson_read_err readErr; yyjson_doc *jsonDoc = yyjson_read_file(path, 0, NULL, &readErr); if(jsonDoc == NULL) { // No settings file yet, leave settings as-is. if(readErr.code == YYJSON_READ_ERROR_FILE_OPEN) errorOk(); errorThrow( "Failed to parse save settings file: %s (%s)", path, readErr.msg ); } yyjson_val *object = yyjson_doc_get_root(jsonDoc); if(object == NULL) { yyjson_doc_free(jsonDoc); errorThrow("Save settings file missing root object: %s", path); } errorret_t readResult = saveSettingsReadJSON(settings, object); yyjson_doc_free(jsonDoc); errorChain(readResult); errorOk(); } errorret_t saveDeviceLinuxDispose(savedevice_t *device) { errorOk(); }