#include "project.hpp" #include "asset-manager.hpp" #include "components.hpp" #include "embedded-shaders.h" #include "pk.h" #include "plugins.hpp" #include "entities.hpp" #include "helpers.hpp" #include "font.hpp" #include "project-settings.hpp" #include #include const long projReadLineLength = 128; char projReadLine[projReadLineLength]; const char* const PKE_PROJ_FILE_BEGIN = ":PKPB:"; const char* const PKE_PROJ_FILE_END = ":PKPE:"; const char* const PKE_PROJ_FILE_VERSION = ":0:"; const char* const PKE_PROJ_FILE_OBJ_END = ""; const char* const PKE_PROJ_FILE_OBJ_PROJECT_SETTINGS = "ProjectSettings:"; const char* const PKE_PROJ_FILE_OBJ_ENTITY_TYPE = "EntityType:"; const char* const PKE_PROJ_FILE_OBJ_ASSET = "Asset:"; const char* const PKE_PROJ_FILE_OBJ_FONT = "Font:"; const char* const PKE_PROJ_FILE_PROJ_SETTINGS_DEFAULT_SCENE_NAME = "PkeSet::DefaultSceneName: "; const char* const PKE_PROJ_FILE_PROJ_SETTINGS_SCENE_COUNT = "PkeSet::SceneCount: "; const char* const PKE_PROJ_FILE_PROJ_SETTINGS_SCENES_BEGIN = "PkeSet::Scenes: ["; const char* const PKE_PROJ_FILE_PROJ_SETTINGS_SCENES_END = "PkeSet::Scenes: ]"; const char* const PKE_PROJ_FILE_ENTITY_TYPE_MODEL_ASSET_KEY = "ModelAssetKey: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE = "EntityTypeCode: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_HANDLE = "EntityHandle: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_UUID = "UUID: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_CREATE_INSTANCE_CALLBACK_SIGNATURE = "InstanceCreateCallbackSignature: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_COLLISION_CALLBACK_SIGNATURE = "CollisionCallbackSignature: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_DETAILS_BEGIN = "EntityHandleDetails: {"; const char* const PKE_PROJ_FILE_ENTITY_TYPE_DETAILS_END = "EntityHandleDetails: }"; const char* const PKE_PROJ_FILE_ENTITY_TYPE_TEXTURE_ASSET_KEY = "TextureAssetKey: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT = "StartingInstanceCount: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_MASS = "BT::StartingMass: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_LAYER = "BT::StartingCollisionLayer: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_MASK = "BT::StartingCollisionMask: "; const char* const PKE_PROJ_FILE_ASSET_KEY = "Asset::Key: "; const char* const PKE_PROJ_FILE_ASSET_BASE_PATH = "Asset::BasePath: "; const char* const PKE_PROJ_FILE_ASSET_TYPE = "Asset::Type: "; const char* const PKE_PROJ_FILE_ASSET_TXTR_WIDTH = "Asset::Width: "; const char* const PKE_PROJ_FILE_ASSET_TXTR_HEIGHT = "Asset::Height: "; /* void Proj_SerializeProjectSettings(std::ostream &stream) { PkeProjectSettings ps{}; if (strncmp(ps.defaultSceneName, pkeProjectSettings.defaultSceneName, strlen(pkeProjectSettings.defaultSceneName)) != 0) { stream << PKE_PROJ_FILE_PROJ_SETTINGS_DEFAULT_SCENE_NAME << ps.defaultSceneName << std::endl; } if (ps.sceneCount != pkeProjectSettings.sceneCount) { stream << PKE_PROJ_FILE_PROJ_SETTINGS_SCENE_COUNT << ps.sceneCount << std::endl; } if (ps.scenes != nullptr) { stream << PKE_PROJ_FILE_PROJ_SETTINGS_SCENES_BEGIN << std::endl; for (long i = 0; i < ps.sceneCount; ++i) { if (ps.scenes[i].length > 0) { stream << ps.scenes[i].val << std::endl; } } stream << PKE_PROJ_FILE_PROJ_SETTINGS_SCENES_END << std::endl; } } */ void Proj_SerializeEntityType(std::ostream &stream, const EntityType &et) { NULL_CHAR_ARR(handleStr, 23); NULL_CHAR_ARR(modelAssetKey, AssetKeyLength + 1); NULL_CHAR_ARR(textureAssetKey, AssetKeyLength + 1); snprintf(handleStr, 22, "0x%08X 0x%08X", et.handle.b, et.handle.i); snprintf(modelAssetKey, AssetKeyLength + 1, "%s", et.modelAssetKey); EntityType e{}; if (modelAssetKey[0] != '\0') stream << PKE_PROJ_FILE_ENTITY_TYPE_MODEL_ASSET_KEY << modelAssetKey << std::endl; if (et.entityTypeCode.val != e.entityTypeCode.val) stream << PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE << et.entityTypeCode.val << std::endl; if (et.handle != e.handle) stream << PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_HANDLE << handleStr << std::endl; if (et.uuid != pk_uuid_zed && et.uuid != pk_uuid_max) { stream << PKE_PROJ_FILE_ENTITY_TYPE_UUID << et.uuid << std::endl; } if (et.createInstanceCallback.name[0] != '\0') { stream << PKE_PROJ_FILE_ENTITY_TYPE_CREATE_INSTANCE_CALLBACK_SIGNATURE << et.createInstanceCallback.name << std::endl; } for (int64_t i = 0; i < et.detailsCount; ++i) { const EntityTypeDetails &etd = et.details[i]; snprintf(textureAssetKey, AssetKeyLength + 1, "%s", etd.textureAssetKey); stream << PKE_PROJ_FILE_ENTITY_TYPE_DETAILS_BEGIN << std::endl; if (textureAssetKey[0] != '\0') stream << PKE_PROJ_FILE_ENTITY_TYPE_TEXTURE_ASSET_KEY << textureAssetKey << std::endl; if (etd.startingInstanceCount != e.details[0].startingInstanceCount) stream << PKE_PROJ_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT << etd.startingInstanceCount << std::endl; if (etd.bt.startingMass != e.details[0].bt.startingMass) stream << PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_MASS << etd.bt.startingMass << std::endl; if (etd.bt.startingCollisionLayer != e.details[0].bt.startingCollisionLayer) stream << PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_LAYER << static_cast(etd.bt.startingCollisionLayer) << std::endl; if (etd.bt.startingCollisionMask != e.details[0].bt.startingCollisionMask) stream << PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_MASK << static_cast(etd.bt.startingCollisionMask) << std::endl; if (etd.grBinds) { if (etd.grBinds->collisionCallback.name[0] != '\0') { stream << PKE_PROJ_FILE_ENTITY_TYPE_COLLISION_CALLBACK_SIGNATURE << etd.grBinds->collisionCallback.name << std::endl; } } stream << PKE_PROJ_FILE_ENTITY_TYPE_DETAILS_END << std::endl; } } void Proj_SerializeAsset(std::ostream &stream, const Asset &asset) { NULL_CHAR_ARR(keyStr, AssetKeyLength + 1); snprintf(keyStr, AssetKeyLength + 1, "%s", asset.key); Asset a{}; if (keyStr[0] != '\0') { stream << PKE_PROJ_FILE_ASSET_KEY << keyStr << std::endl; } if (asset.basePath != nullptr) { stream << PKE_PROJ_FILE_ASSET_BASE_PATH << asset.basePath << std::endl; } if (asset.type != a.type) { stream << PKE_PROJ_FILE_ASSET_TYPE << static_cast(asset.type) << std::endl; } switch (asset.type) { case PKE_ASSET_TYPE_TEXTURE: stream << PKE_PROJ_FILE_ASSET_TXTR_WIDTH << asset.details.texture.width << std::endl; stream << PKE_PROJ_FILE_ASSET_TXTR_HEIGHT << asset.details.texture.height << std::endl; break; default: /* no-op */ break; } } /* void Proj_DeserializeProjectSettings(std::istream &stream) { while (memset(projReadLine, 0, projReadLineLength), stream.getline(projReadLine, projReadLineLength)) { if (strcmp(PKE_PROJ_FILE_OBJ_END, projReadLine) == 0) { return; } if (strncmp(projReadLine, PKE_PROJ_FILE_PROJ_SETTINGS_DEFAULT_SCENE_NAME, strlen(PKE_PROJ_FILE_PROJ_SETTINGS_DEFAULT_SCENE_NAME)) == 0) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_PROJ_SETTINGS_DEFAULT_SCENE_NAME); uint64_t len = strlen(projReadLine + prefixLen) + 1; char *val = pk_new_arr(len); memset(reinterpret_cast(val), '\0', len); memcpy(val, projReadLine + prefixLen, len); pkeProjectSettings.defaultSceneName = val; continue; } if (strncmp(projReadLine, PKE_PROJ_FILE_PROJ_SETTINGS_SCENES_BEGIN, strlen(PKE_PROJ_FILE_PROJ_SETTINGS_SCENES_BEGIN)) == 0) { pk_arr_t sceneFiles; while (memset(projReadLine, 0, projReadLineLength), stream.getline(projReadLine, projReadLineLength)) { if (strcmp(PKE_PROJ_FILE_PROJ_SETTINGS_SCENES_END, projReadLine) == 0) { pkeProjectSettings.sceneCount = sceneFiles.next; pkeProjectSettings.scenes = pk_new_arr(sceneFiles.next); memcpy(reinterpret_cast(pkeProjectSettings.scenes), reinterpret_cast(sceneFiles.data), sizeof(pk_cstr) * sceneFiles.next); break; } pkeProjectSettings.sceneCount += 1; uint64_t len = strlen(projReadLine) + 1; char *val = pk_new_arr(len); memset(val, '\0', len); memcpy(val, projReadLine, len - 1); auto &str = sceneFiles.Push(); str.length = len - 1; str.val = val; } } } } */ void Proj_DeserializeEntityType(std::istream &stream) { char collisionSig[CallbackSignatureLength + 1]; collisionSig[0] = '\0'; char createInstanceSig[CallbackSignatureLength + 1]; createInstanceSig[0] = '\0'; EntityType et{}; int64_t detailCount = 0; while (memset(projReadLine, 0, projReadLineLength), stream.getline(projReadLine, projReadLineLength)) { if (strcmp(PKE_PROJ_FILE_OBJ_END, projReadLine) == 0) { EntityType *existingPtr = EntityType_FindByTypeCode(et.entityTypeCode.val); if (existingPtr != nullptr) { if (et.entityTypeCode.reserved > 0) { pk_delete_arr(et.entityTypeCode.val, et.entityTypeCode.reserved); } continue; } EntityType *etPtr = EntityType_Create(et.uuid); strncpy(etPtr->modelAssetKey, et.modelAssetKey, AssetKeyLength); etPtr->entityTypeCode = et.entityTypeCode; if (createInstanceSig[0] == '\0') { strncpy(etPtr->createInstanceCallback.name, createInstanceSig, CallbackSignatureLength); PkePlugin_SetSignatureFunc(&etPtr->createInstanceCallback); } else { etPtr->createInstanceCallback.name[0] = 'd'; etPtr->createInstanceCallback.func = reinterpret_cast(EntityType_CreateGenericInstance); } for (int64_t i = 0; i < detailCount; ++i) { etPtr->details[i] = et.details[i]; strncpy(etPtr->details[i].textureAssetKey, et.details[i].textureAssetKey, AssetKeyLength); } EntityType_Load(*etPtr); for (int64_t i = 0; i < detailCount; ++i) { strncpy(etPtr->details[i].grBinds->collisionCallback.name, collisionSig, CallbackSignatureLength); PkePlugin_SetSignatureFunc(&etPtr->details[i].grBinds->collisionCallback); } return; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_MODEL_ASSET_KEY)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_MODEL_ASSET_KEY); strncpy(et.modelAssetKey, projReadLine + prefixLen, AssetKeyLength); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE); et.entityTypeCode.length = strlen(projReadLine + prefixLen) + 1; et.entityTypeCode.reserved = et.entityTypeCode.length + 1; char *val = pk_new_arr(et.entityTypeCode.reserved); snprintf(val, et.entityTypeCode.reserved, "%s", projReadLine + prefixLen); et.entityTypeCode.val = val; continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_HANDLE)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_HANDLE); // 0x00000000 0x00000000 projReadLine[prefixLen + 10] = '\0'; unsigned int b, i; STR2NUM_ERROR result1 = str2num(b, projReadLine + prefixLen + 2, 16); STR2NUM_ERROR result2 = str2num(i, projReadLine + prefixLen + 11, 16); et.handle = EntityHandle { b, i }; assert(result1 == STR2NUM_ERROR::SUCCESS); assert(result2 == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_UUID)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_UUID); (projReadLine + prefixLen) >> et.uuid; continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_CREATE_INSTANCE_CALLBACK_SIGNATURE)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_CREATE_INSTANCE_CALLBACK_SIGNATURE); strncpy(createInstanceSig, projReadLine + prefixLen, CallbackSignatureLength + 1); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_DETAILS_BEGIN)) { EntityTypeDetails &etd = et.details[detailCount]; while (memset(projReadLine, 0, projReadLineLength), stream.getline(projReadLine, projReadLineLength)) { if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_DETAILS_END)) { detailCount += 1; break; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_TEXTURE_ASSET_KEY)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_TEXTURE_ASSET_KEY); strncpy(etd.textureAssetKey, projReadLine + prefixLen, AssetKeyLength); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT); STR2NUM_ERROR result = str2num(etd.startingInstanceCount, projReadLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_MASS)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_MASS); STR2NUM_ERROR result = str2num(etd.bt.startingMass, projReadLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_LAYER)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_LAYER); PhysicsCollision_T val = static_cast(etd.bt.startingCollisionLayer); STR2NUM_ERROR result = str2num(val, projReadLine + prefixLen); etd.bt.startingCollisionLayer = PhysicsCollision{val}; assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_MASK)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_MASK); PhysicsCollision_T val = static_cast(etd.bt.startingCollisionMask); STR2NUM_ERROR result = str2num(val, projReadLine + prefixLen); etd.bt.startingCollisionMask = PhysicsCollision{val}; assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_COLLISION_CALLBACK_SIGNATURE)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_COLLISION_CALLBACK_SIGNATURE); strncpy(collisionSig, projReadLine + prefixLen, CallbackSignatureLength + 1); continue; } } continue; } } } void Proj_DeserializeAssset(std::istream &stream) { size_t prefixLen, strLen; AssetKey keyStr; union pke_asset_details details; memset(&details, 0, sizeof(union pke_asset_details)); keyStr[AssetKeyLength-1] = '\0'; const size_t path_len = 256; char basePath[path_len]; basePath[0] = '\0'; basePath[path_len-1] = '\0'; AssetType at{PKE_ASSET_TYPE_UNSET}; PK_STN_RES result; while (memset(projReadLine, 0, projReadLineLength), stream.getline(projReadLine, projReadLineLength)) { if (strcmp(projReadLine, PKE_PROJ_FILE_OBJ_END) == 0) { AM_Register(keyStr, at, basePath, &details); return; } if (strstr(projReadLine, PKE_PROJ_FILE_ASSET_KEY) != nullptr) { prefixLen = strlen(PKE_PROJ_FILE_ASSET_KEY); strLen = strlen(projReadLine) - prefixLen; memset(keyStr, '\0', AssetKeyLength); memcpy(keyStr, projReadLine + prefixLen, PK_MIN(AssetKeyLength, strLen)); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ASSET_BASE_PATH) != nullptr) { prefixLen = strlen(PKE_PROJ_FILE_ASSET_BASE_PATH); uint64_t strLen = strlen(projReadLine) - prefixLen; strncpy(basePath, projReadLine + prefixLen, PK_MIN(strLen + 1, path_len)); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ASSET_TYPE) != nullptr) { prefixLen = strlen(PKE_PROJ_FILE_ASSET_TYPE); AssetType_T val{}; result = pk_stn(&val, projReadLine + prefixLen, nullptr); at = AssetType{val}; assert(result == PK_STN_RES_SUCCESS); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ASSET_TXTR_WIDTH) != nullptr) { prefixLen = strlen(PKE_PROJ_FILE_ASSET_TXTR_WIDTH); result = pk_stn(&details.texture.width, projReadLine + prefixLen, nullptr); assert(result == PK_STN_RES_SUCCESS); continue; } if (strstr(projReadLine, PKE_PROJ_FILE_ASSET_TXTR_HEIGHT) != nullptr) { prefixLen = strlen(PKE_PROJ_FILE_ASSET_TXTR_HEIGHT); result = pk_stn(&details.texture.height, projReadLine + prefixLen, nullptr); assert(result == PK_STN_RES_SUCCESS); continue; } } } void PkeProject_Load(const char *filePath) { const char *safeFilePath = filePath == nullptr ? PKE_PROJ_DEFAULT_FILENAME : filePath; std::ifstream f(safeFilePath); if (!f.is_open()) { fprintf(stderr, "While attempting to load project file, failed to open requested file for reading: %s\n", safeFilePath); return; } memset(projReadLine, '\0', projReadLineLength); while (memset(projReadLine, 0, projReadLineLength), f.getline(projReadLine, projReadLineLength)) { /* if (strcmp(PKE_PROJ_FILE_OBJ_PROJECT_SETTINGS, projReadLine) == 0) { Proj_DeserializeProjectSettings(f); continue; } */ if (strcmp(PKE_PROJ_FILE_OBJ_ASSET, projReadLine) == 0) { Proj_DeserializeAssset(f); continue; } if (strcmp(PKE_PROJ_FILE_OBJ_ENTITY_TYPE, projReadLine) == 0) { Proj_DeserializeEntityType(f); continue; } if (strcmp(PKE_PROJ_FILE_OBJ_FONT, projReadLine) == 0) { FontType_Deserialize(f); continue; } } f.close(); } void PkeProject_Save(const char *filePath) { bool failed = false; bool b; pk_iter_t iter_asset{}; pk_iter_t iter_ent_type{}; pk_iter_t iter_font_type{}; pk_bkt_arr *bkt_arr_assets; pk_bkt_arr *bkt_arr_ent_types; pk_bkt_arr *bkt_arr_font_types; const char *saveFilePath = filePath == nullptr ? PKE_PROJ_DEFAULT_FILENAME : filePath; std::ostringstream stream{}; try { stream << PKE_PROJ_FILE_BEGIN << std::endl; stream << PKE_PROJ_FILE_VERSION << std::endl; stream << "" << std::endl; /* f << PKE_PROJ_FILE_OBJ_PROJECT_SETTINGS << std::endl; Proj_SerializeProjectSettings(f); f << PKE_PROJ_FILE_OBJ_END << std::endl; */ bkt_arr_assets = AM_GetAssets(); b = pk_bkt_arr_iter_begin(bkt_arr_assets, &iter_asset); while (b == true) { if (PK_HAS_FLAG(iter_asset->flags, PKE_ASSET_FLAGS_MEM_STATIC)) { b = pk_bkt_arr_iter_increment(bkt_arr_assets, &iter_asset); continue; } // TODO 2025-05-30 JCB // This should be a flag bool isGlobalAsset = false; for (long k = 0; k < embedded_shader_index_count; ++k) { if (strncmp(embedded_shaders[k].name, iter_asset->key, AssetKeyLength) == 0) { isGlobalAsset = true; break; } } if (isGlobalAsset) { b = pk_bkt_arr_iter_increment(bkt_arr_assets, &iter_asset); continue; } stream << PKE_PROJ_FILE_OBJ_ASSET << std::endl; Proj_SerializeAsset(stream, *iter_asset); stream << PKE_PROJ_FILE_OBJ_END << std::endl; b = pk_bkt_arr_iter_increment(bkt_arr_assets, &iter_asset); } bkt_arr_ent_types = EntityType_GetEntityTypes(); b = pk_bkt_arr_iter_begin(bkt_arr_ent_types, &iter_ent_type); while (b == true) { if (iter_ent_type->modelAssetKey[0] == '\0') { b = pk_bkt_arr_iter_increment(bkt_arr_ent_types, &iter_ent_type); continue; } stream << PKE_PROJ_FILE_OBJ_ENTITY_TYPE << std::endl; Proj_SerializeEntityType(stream, *iter_ent_type); stream << PKE_PROJ_FILE_OBJ_END << std::endl; b = pk_bkt_arr_iter_increment(bkt_arr_ent_types, &iter_ent_type); } bkt_arr_font_types = FontType_GetFonts(); b = pk_bkt_arr_iter_begin(bkt_arr_font_types, &iter_font_type); while(b) { if (PK_HAS_FLAG(iter_font_type->entity_flags, ENTITY_FLAG_DO_NOT_SERIALIZE)) { b = pk_bkt_arr_iter_increment(bkt_arr_font_types, &iter_font_type); continue; } stream << PKE_PROJ_FILE_OBJ_FONT << std::endl; FontType_Serialize(stream, iter_font_type); stream << PKE_PROJ_FILE_OBJ_END << std::endl; b = pk_bkt_arr_iter_increment(bkt_arr_font_types, &iter_font_type); } stream << PKE_PROJ_FILE_END << std::endl; } catch (std::exception &e) { fprintf(stderr, "[%s][PkeProject_Save] Failed to serialize project file: %s\n", __FILE__, e.what()); failed = false; } catch (...) { fprintf(stderr, "[%s][PkeProject_Save] Failed to serialize project file, uncaught exception.\n", __FILE__); failed = false; } if (failed == false) { std::ofstream f(saveFilePath); if (!f.is_open()) { failed = true; } else { f << stream.str(); } f.flush(); f.close(); } if (failed) { NULL_CHAR_ARR(errFileName, 256); strncpy(errFileName, saveFilePath, 256); strncpy(errFileName + strlen(saveFilePath), ".err", 256 - strlen(saveFilePath)); std::ofstream errF(saveFilePath); if (errF.is_open()) { errF << stream.str(); errF.flush(); errF.close(); fprintf(stderr, "Failed to save Projection file '%s', partial output saved to '%s'\n", saveFilePath, errFileName); } else { fprintf(stderr, "Failed to save Projection file '%s' and also failed to write failed output\n", saveFilePath); } } }