/* Copyright (c) 2023 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "dynamictypes/DataSerializer.h" #include "tileset/TilesetEditData.h" #include "utilities/IOUtilities.h" #include #include TilesetEditData * TilesetEditData_create(TilesetID identifier, const char * name, Vector2u tileSize, Vector2i origin, unsigned int tileCount, TileEditData * tiles) { TilesetEditData * tileset = malloc(sizeof(*tileset)); tileset->identifier = identifier; tileset->name = strdup_nullSafe(name); tileset->tileSize = tileSize; tileset->origin = origin; tileset->tileCount = tileCount; tileset->tiles = NULL; if (tileCount > 0) { tileset->tiles = malloc(tileCount * sizeof(*tileset->tiles)); for (unsigned int tileIndex = 0; tileIndex < tileCount; tileIndex++) { tileset->tiles[tileIndex] = tiles[tileIndex]; tileset->tiles[tileIndex].name = strdup(tiles[tileIndex].name); if (tiles[tileIndex].metadata != NULL) { tileset->tiles[tileIndex].metadata = hashCopy(tiles[tileIndex].metadata); } } } tileset->metadataSchemaCount = 0; tileset->metadataSchemas = NULL; tileset->enumerationCount = 0; tileset->enumerations = NULL; tileset->tileSchemaID = METADATA_SCHEMA_ID_NONE; tileset->tileMapSchemaID = METADATA_SCHEMA_ID_NONE; tileset->tileMapLayerSchemaID = METADATA_SCHEMA_ID_NONE; tileset->tileZoneSchemaID = METADATA_SCHEMA_ID_NONE; return tileset; } TilesetEditData * TilesetEditData_copy(TilesetEditData * tileset) { TilesetEditData * copy = TilesetEditData_create(tileset->identifier, tileset->name, tileset->tileSize, tileset->origin, tileset->tileCount, tileset->tiles); if (tileset->metadataSchemaCount > 0) { copy->metadataSchemaCount = tileset->metadataSchemaCount; copy->metadataSchemas = malloc(copy->metadataSchemaCount * sizeof(*copy->metadataSchemas)); for (unsigned int schemaIndex = 0; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { copy->metadataSchemas[schemaIndex] = tileset->metadataSchemas[schemaIndex]; copy->metadataSchemas[schemaIndex].name = strdup(tileset->metadataSchemas[schemaIndex].name); copy->metadataSchemas[schemaIndex].schema = DataValueSchema_copy(tileset->metadataSchemas[schemaIndex].schema); } } if (tileset->enumerationCount > 0) { copy->enumerationCount = tileset->enumerationCount; copy->enumerations = malloc(tileset->enumerationCount * sizeof(*copy->enumerations)); for (unsigned int enumerationIndex = 0; enumerationIndex < tileset->enumerationCount; enumerationIndex++) { copy->enumerations[enumerationIndex] = tileset->enumerations[enumerationIndex]; copy->enumerations[enumerationIndex].name = strdup(tileset->enumerations[enumerationIndex].name); copy->enumerations[enumerationIndex].valueCount = tileset->enumerations[enumerationIndex].valueCount; copy->enumerations[enumerationIndex].values = NULL; if (tileset->enumerations[enumerationIndex].valueCount > 0) { copy->enumerations[enumerationIndex].values = malloc(tileset->enumerations[enumerationIndex].valueCount * sizeof(*tileset->enumerations[enumerationIndex].values)); for (unsigned int valueIndex = 0; valueIndex < tileset->enumerations[enumerationIndex].valueCount; valueIndex++) { copy->enumerations[enumerationIndex].values[valueIndex].name = strdup(tileset->enumerations[enumerationIndex].values[valueIndex].name); copy->enumerations[enumerationIndex].values[valueIndex].value = tileset->enumerations[enumerationIndex].values[valueIndex].value; } } } } copy->tileSchemaID = tileset->tileSchemaID; copy->tileMapSchemaID = tileset->tileMapSchemaID; copy->tileMapLayerSchemaID = tileset->tileMapLayerSchemaID; copy->tileZoneSchemaID = tileset->tileZoneSchemaID; return copy; } void TilesetEditData_dispose(TilesetEditData * tileset) { free(tileset->name); for (unsigned int tileIndex = 0; tileIndex < tileset->tileCount; tileIndex++) { free(tileset->tiles[tileIndex].name); if (tileset->tiles[tileIndex].metadata != NULL) { hashDispose(tileset->tiles[tileIndex].metadata); } } free(tileset->tiles); for (unsigned int schemaIndex = 0; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { free(tileset->metadataSchemas[schemaIndex].name); DataValueSchema_dispose(tileset->metadataSchemas[schemaIndex].schema); } free(tileset->metadataSchemas); for (unsigned int enumerationIndex = 0; enumerationIndex < tileset->enumerationCount; enumerationIndex++) { free(tileset->enumerations[enumerationIndex].name); for (unsigned int valueIndex = 0; valueIndex < tileset->enumerations[enumerationIndex].valueCount; valueIndex++) { free(tileset->enumerations[enumerationIndex].values[valueIndex].name); } free(tileset->enumerations[enumerationIndex].values); } free(tileset->enumerations); free(tileset); } void TilesetEditData_serializeMinimal(TilesetEditData * tileset, compat_type(SerializationContext *) serializationContext) { SerializationContext * context = serializationContext; DataValueSchema * tileSchema = TilesetEditData_getMetadataSchema(tileset, tileset->tileSchemaID); call_virtual(writeUInt32, context, "id", tileset->identifier); call_virtual(writeString, context, "name", tileset->name); call_virtual(writeUInt16, context, "size_x", tileset->tileSize.x); call_virtual(writeUInt16, context, "size_y", tileset->tileSize.y); call_virtual(writeInt16, context, "origin_x", tileset->origin.x); call_virtual(writeInt16, context, "origin_y", tileset->origin.y); call_virtual(beginArray, context, "enumerations"); for (unsigned int enumerationIndex = 0; enumerationIndex < tileset->enumerationCount; enumerationIndex++) { call_virtual(beginStructure, context, NULL); call_virtual(writeUInt32, context, "id", tileset->enumerations[enumerationIndex].identifier); call_virtual(writeString, context, "name", tileset->enumerations[enumerationIndex].name); call_virtual(beginArray, context, "values"); for (unsigned int valueIndex = 0; valueIndex < tileset->enumerations[enumerationIndex].valueCount; valueIndex++) { call_virtual(beginStructure, context, NULL); call_virtual(writeString, context, "name", tileset->enumerations[enumerationIndex].values[valueIndex].name); call_virtual(writeInt32, context, "value", tileset->enumerations[enumerationIndex].values[valueIndex].value); call_virtual(endStructure, context); } call_virtual(endArray, context); call_virtual(endStructure, context); } call_virtual(endArray, context); call_virtual(writeUInt16, context, "schema_format_version", DATA_VALUE_SCHEMA_FORMAT_VERSION); call_virtual(beginArray, context, "schemas"); for (unsigned int schemaIndex = 0; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { call_virtual(beginStructure, context, NULL); call_virtual(writeUInt32, context, "id", tileset->metadataSchemas[schemaIndex].identifier); call_virtual(writeString, context, "name", tileset->metadataSchemas[schemaIndex].name); DataValueSchema_serializeMinimal(tileset->metadataSchemas[schemaIndex].schema, context); call_virtual(endStructure, context); } call_virtual(endArray, context); call_virtual(writeUInt32, context, "tile_schema_id", tileset->tileSchemaID); call_virtual(writeUInt32, context, "tile_map_schema_id", tileset->tileMapSchemaID); call_virtual(writeUInt32, context, "tile_map_layer_schema_id", tileset->tileMapLayerSchemaID); call_virtual(writeUInt32, context, "tile_zone_schema_id", tileset->tileZoneSchemaID); call_virtual(beginArray, context, "tiles"); for (unsigned int tileIndex = 0; tileIndex < tileset->tileCount; tileIndex++) { call_virtual(beginStructure, context, NULL); call_virtual(writeUInt32, context, "id", tileset->tiles[tileIndex].identifier); call_virtual(writeString, context, "name", tileset->tiles[tileIndex].name); call_virtual(writeUInt32, context, "image_id", tileset->tiles[tileIndex].imageID); call_virtual(writeInt16, context, "offset_x", tileset->tiles[tileIndex].offset.x); call_virtual(writeInt16, context, "offset_y", tileset->tiles[tileIndex].offset.y); call_virtual(writeUInt32, context, "instance_schema_id", tileset->tiles[tileIndex].instanceSchemaID); if (tileSchema != NULL) { DataValue metadataValue = valueCreateHashTable(tileset->tiles[tileIndex].metadata, false, false); DataValue_serializeWithSchema(&metadataValue, tileSchema, "metadata", context); } call_virtual(endStructure, context); } call_virtual(endArray, context); } void TilesetEditData_serialize(TilesetEditData * tileset, compat_type(SerializationContext *) serializationContext) { serialize_implementation_v2(TilesetEditData, tileset, TILESET_EDIT_DATA); } TilesetEditData * TilesetEditData_deserializeMinimal(compat_type(DeserializationContext *) deserializationContext, uint16_t formatVersion) { DeserializationContext * context = deserializationContext; if (context->status != SERIALIZATION_ERROR_OK) { return NULL; } if (formatVersion > TILESET_EDIT_DATA_FORMAT_VERSION) { context->status = SERIALIZATION_ERROR_FORMAT_VERSION_TOO_NEW; return NULL; } TilesetEditData * tileset = TilesetEditData_create(0, NULL, VECTOR2u_ZERO, VECTOR2i_ZERO, 0, NULL); tileset->identifier = call_virtual(readUInt32, context, "id"); tileset->name = strdup_nullSafe(call_virtual(readString, context, "name")); tileset->tileSize.x = call_virtual(readUInt16, context, "size_x"); tileset->tileSize.y = call_virtual(readUInt16, context, "size_y"); tileset->origin.x = call_virtual(readInt16, context, "origin_x"); tileset->origin.y = call_virtual(readInt16, context, "origin_y"); if (tileset->tileSize.x == 0 || tileset->tileSize.x > TILESET_TILE_DIMENSION_MAX || tileset->tileSize.y == 0 || tileset->tileSize.y > TILESET_TILE_DIMENSION_MAX) { TilesetEditData_dispose(tileset); return NULL; } tileset->enumerationCount = call_virtual(beginArray, context, "enumerations"); if (tileset->enumerationCount > 0) { if (tileset->enumerationCount > TILESET_ENUMERATION_COUNT_MAX) { tileset->enumerationCount = 0; TilesetEditData_dispose(tileset); return NULL; } tileset->enumerations = calloc(tileset->enumerationCount, sizeof(*tileset->enumerations)); for (unsigned int enumerationIndex = 0; enumerationIndex < tileset->enumerationCount; enumerationIndex++) { call_virtual(beginStructure, context, NULL); tileset->enumerations[enumerationIndex].identifier = call_virtual(readUInt32, context, "id"); tileset->enumerations[enumerationIndex].name = strdup_nullSafe(call_virtual(readString, context, "name")); tileset->enumerations[enumerationIndex].valueCount = call_virtual(beginArray, context, "values"); if (tileset->enumerations[enumerationIndex].valueCount > 0) { if (tileset->enumerations[enumerationIndex].valueCount > TILESET_ENUMERATION_VALUE_COUNT_MAX) { tileset->enumerations[enumerationIndex].valueCount = 0; TilesetEditData_dispose(tileset); return NULL; } tileset->enumerations[enumerationIndex].values = calloc(tileset->enumerations[enumerationIndex].valueCount, sizeof(*tileset->enumerations[enumerationIndex].values)); for (unsigned int valueIndex = 0; valueIndex < tileset->enumerations[enumerationIndex].valueCount; valueIndex++) { call_virtual(beginStructure, context, NULL); tileset->enumerations[enumerationIndex].values[valueIndex].name = strdup_nullSafe(call_virtual(readString, context, "name")); tileset->enumerations[enumerationIndex].values[valueIndex].value = call_virtual(readInt32, context, "value"); call_virtual(endStructure, context); } } call_virtual(endArray, context); call_virtual(endStructure, context); } } call_virtual(endArray, context); uint16_t dataValueSchemaFormatVersion = call_virtual(readUInt16, context, "schema_format_version"); tileset->metadataSchemaCount = call_virtual(beginArray, context, "schemas"); if (tileset->metadataSchemaCount > 0) { if (tileset->metadataSchemaCount > TILESET_METADATA_SCHEMA_COUNT_MAX) { tileset->metadataSchemaCount = 0; TilesetEditData_dispose(tileset); return NULL; } tileset->metadataSchemas = calloc(tileset->metadataSchemaCount, sizeof(*tileset->metadataSchemas)); for (unsigned int schemaIndex = 0; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { call_virtual(beginStructure, context, NULL); tileset->metadataSchemas[schemaIndex].identifier = call_virtual(readUInt32, context, "id"); tileset->metadataSchemas[schemaIndex].name = strdup_nullSafe(call_virtual(readString, context, "name")); tileset->metadataSchemas[schemaIndex].dirtyValue = 0; tileset->metadataSchemas[schemaIndex].schema = DataValueSchema_deserializeMinimal(context, dataValueSchemaFormatVersion); call_virtual(endStructure, context); } } call_virtual(endArray, context); tileset->tileSchemaID = call_virtual(readUInt32, context, "tile_schema_id"); tileset->tileMapSchemaID = call_virtual(readUInt32, context, "tile_map_schema_id"); tileset->tileMapLayerSchemaID = call_virtual(readUInt32, context, "tile_map_layer_schema_id"); if (formatVersion > 0) { tileset->tileZoneSchemaID = call_virtual(readUInt32, context, "tile_zone_schema_id"); } else { tileset->tileZoneSchemaID = METADATA_SCHEMA_ID_NONE; } DataValueSchema * tileSchema = TilesetEditData_getMetadataSchema(tileset, tileset->tileSchemaID); tileset->tileCount = call_virtual(beginArray, context, "tiles"); if (tileset->tileCount > 0) { if (tileset->tileCount > TILESET_TILE_COUNT_MAX) { tileset->tileCount = 0; TilesetEditData_dispose(tileset); return NULL; } tileset->tiles = calloc(tileset->tileCount, sizeof(*tileset->tiles)); for (unsigned int tileIndex = 0; tileIndex < tileset->tileCount; tileIndex++) { call_virtual(beginStructure, context, NULL); tileset->tiles[tileIndex].identifier = call_virtual(readUInt32, context, "id"); tileset->tiles[tileIndex].name = strdup_nullSafe(call_virtual(readString, context, "name")); tileset->tiles[tileIndex].imageID = call_virtual(readUInt32, context, "image_id"); tileset->tiles[tileIndex].offset.x = call_virtual(readInt16, context, "offset_x"); tileset->tiles[tileIndex].offset.y = call_virtual(readInt16, context, "offset_y"); tileset->tiles[tileIndex].instanceSchemaID = call_virtual(readUInt32, context, "instance_schema_id"); if (tileSchema != NULL) { DataValue metadataValue = DataValue_deserializeWithSchema(tileSchema, "metadata", DATA_TYPE_HASH_TABLE, context, false); if (metadataValue.type != DATA_TYPE_HASH_TABLE) { TilesetEditData_dispose(tileset); return NULL; } tileset->tiles[tileIndex].metadata = metadataValue.value.hashTable; } call_virtual(endStructure, context); } } call_virtual(endArray, context); if (context->status != SERIALIZATION_ERROR_OK) { TilesetEditData_dispose(tileset); return NULL; } return tileset; } TilesetEditData * TilesetEditData_deserialize(compat_type(DeserializationContext *) deserializationContext) { deserialize_implementation_v2(TilesetEditData, TILESET_EDIT_DATA); } TileID TilesetEditData_getUnusedTileID(TilesetEditData * tileset, TileID afterTileID) { if (afterTileID == UINT32_MAX) { TileID identifierMax = 0; for (unsigned int tileIndex = 0; tileIndex < tileset->tileCount; tileIndex++) { if (tileset->tiles[tileIndex].identifier > identifierMax) { identifierMax = tileset->tiles[tileIndex].identifier; } } return identifierMax + 1; } TileID closestTileID = afterTileID + 1; bool occupied; do { occupied = false; for (unsigned int tileIndex = 0; tileIndex < tileset->tileCount; tileIndex++) { if (tileset->tiles[tileIndex].identifier == closestTileID) { occupied = true; closestTileID++; } } } while (occupied); return closestTileID; } void TilesetEditData_addTile(TilesetEditData * tileset, TileEditData tile) { TilesetEditData_insertTile(tileset, tileset->tileCount, tile); } void TilesetEditData_insertTile(TilesetEditData * tileset, unsigned int tileIndex, TileEditData tile) { assert(tileIndex <= tileset->tileCount); tileset->tiles = realloc(tileset->tiles, (tileset->tileCount + 1) * sizeof(*tileset->tiles)); for (unsigned int tileIndex2 = tileset->tileCount; tileIndex2 > tileIndex; tileIndex2--) { tileset->tiles[tileIndex2] = tileset->tiles[tileIndex2 - 1]; } tileset->tiles[tileIndex] = tile; tileset->tileCount++; } TileEditData * TilesetEditData_getTile(TilesetEditData * tileset, TileID tileID) { if (tileID == TILE_ID_NONE || tileID > TILE_ID_MAX) { return NULL; } for (unsigned int tileIndex = 0; tileIndex < tileset->tileCount; tileIndex++) { if (tileset->tiles[tileIndex].identifier == tileID) { return &tileset->tiles[tileIndex]; } } return NULL; } unsigned int TilesetEditData_getTileIndex(TilesetEditData * tileset, TileID tileID) { if (tileID == TILE_ID_NONE || tileID > TILE_ID_MAX) { return UINT_MAX; } for (unsigned int tileIndex = 0; tileIndex < tileset->tileCount; tileIndex++) { if (tileset->tiles[tileIndex].identifier == tileID) { return tileIndex; } } return UINT_MAX; } void TilesetEditData_removeTileAtIndex(TilesetEditData * tileset, unsigned int tileIndex) { if (tileIndex < tileset->tileCount) { free(tileset->tiles[tileIndex].name); tileset->tileCount--; for (; tileIndex < tileset->tileCount; tileIndex++) { tileset->tiles[tileIndex] = tileset->tiles[tileIndex + 1]; } } } void TilesetEditData_reorderTile(TilesetEditData * tileset, unsigned int fromTileIndex, unsigned int toTileIndex) { assert(fromTileIndex < tileset->tileCount); assert(toTileIndex < tileset->tileCount); TileEditData swap = tileset->tiles[fromTileIndex]; int direction = (toTileIndex > fromTileIndex) * 2 - 1; for (unsigned int swapIndex = fromTileIndex; swapIndex != toTileIndex; swapIndex += direction) { tileset->tiles[swapIndex] = tileset->tiles[swapIndex + direction]; } tileset->tiles[toTileIndex] = swap; } static const char * getTileNameAtIndex(unsigned int index, void * context) { TilesetEditData * tileset = context; return tileset->tiles[index].name; } TileEditData TilesetEditData_newTile(TilesetEditData * tileset, const char * defaultName, TileID afterTileID) { TileEditData tile; tile.identifier = TilesetEditData_getUnusedTileID(tileset, afterTileID); if (defaultName == NULL) { defaultName = "tile"; } tile.name = newDisplayStringWithNumberSuffix(defaultName, 32, tileset->tileCount, getTileNameAtIndex, tileset); tile.imageID = IMAGE_ID_NONE; tile.offset = VECTOR2i_ZERO; tile.instanceSchemaID = METADATA_SCHEMA_ID_NONE; DataValueSchema * schema = TilesetEditData_getMetadataSchema(tileset, tileset->tileSchemaID); if (schema == NULL) { tile.metadata = NULL; } else { tile.metadata = DataValueSchema_createBlankValue(schema, DATA_TYPE_HASH_TABLE).value.hashTable; } return tile; } TilesetEnumerationID TilesetEditData_getUnusedEnumerationID(TilesetEditData * tileset) { TilesetEnumerationID identifierMax = 0; for (unsigned int enumerationIndex = 0; enumerationIndex < tileset->enumerationCount; enumerationIndex++) { if (tileset->enumerations[enumerationIndex].identifier > identifierMax) { identifierMax = tileset->enumerations[enumerationIndex].identifier; } } return identifierMax + 1; } void TilesetEditData_addEnumeration(TilesetEditData * tileset, TilesetEnumeration enumeration) { TilesetEditData_insertEnumeration(tileset, tileset->enumerationCount, enumeration); } void TilesetEditData_insertEnumeration(TilesetEditData * tileset, unsigned int enumerationIndex, TilesetEnumeration enumeration) { assert(enumerationIndex <= tileset->enumerationCount); tileset->enumerations = realloc(tileset->enumerations, (tileset->enumerationCount + 1) * sizeof(*tileset->enumerations)); for (unsigned int enumerationIndex2 = tileset->enumerationCount; enumerationIndex2 > enumerationIndex; enumerationIndex2--) { tileset->enumerations[enumerationIndex2] = tileset->enumerations[enumerationIndex2 - 1]; } tileset->enumerations[enumerationIndex] = enumeration; tileset->enumerationCount++; } TilesetEnumeration * TilesetEditData_getEnumeration(TilesetEditData * tileset, TilesetEnumerationID enumerationID) { for (unsigned int enumerationIndex = 0; enumerationIndex < tileset->enumerationCount; enumerationIndex++) { if (tileset->enumerations[enumerationIndex].identifier == enumerationID) { return &tileset->enumerations[enumerationIndex]; } } return NULL; } void TilesetEditData_removeEnumerationAtIndex(TilesetEditData * tileset, unsigned int enumerationIndex) { if (enumerationIndex < tileset->enumerationCount) { free(tileset->enumerations[enumerationIndex].name); for (unsigned int valueIndex = 0; valueIndex < tileset->enumerations[enumerationIndex].valueCount; valueIndex++) { free(tileset->enumerations[enumerationIndex].values[valueIndex].name); } free(tileset->enumerations[enumerationIndex].values); tileset->enumerationCount--; for (; enumerationIndex < tileset->enumerationCount; enumerationIndex++) { tileset->enumerations[enumerationIndex] = tileset->enumerations[enumerationIndex + 1]; } } } void TilesetEditData_reorderEnumeration(TilesetEditData * tileset, unsigned int fromEnumerationIndex, unsigned int toEnumerationIndex) { assert(fromEnumerationIndex < tileset->enumerationCount); assert(toEnumerationIndex < tileset->enumerationCount); TilesetEnumeration swap = tileset->enumerations[fromEnumerationIndex]; int direction = (toEnumerationIndex > fromEnumerationIndex) * 2 - 1; for (unsigned int swapIndex = fromEnumerationIndex; swapIndex != toEnumerationIndex; swapIndex += direction) { tileset->enumerations[swapIndex] = tileset->enumerations[swapIndex + direction]; } tileset->enumerations[toEnumerationIndex] = swap; } static const char * getEnumerationNameAtIndex(unsigned int index, void * context) { TilesetEditData * tileset = context; return tileset->enumerations[index].name; } TilesetEnumeration TilesetEditData_newEnumeration(TilesetEditData * tileset, const char * defaultName) { TilesetEnumeration enumeration; enumeration.identifier = TilesetEditData_getUnusedEnumerationID(tileset); if (defaultName == NULL) { defaultName = "enumeration"; } enumeration.name = newDisplayStringWithNumberSuffix(defaultName, 32, tileset->enumerationCount, getEnumerationNameAtIndex, tileset); enumeration.valueCount = 0; enumeration.values = NULL; return enumeration; } TilesetMetadataSchemaID TilesetEditData_getUnusedMetadataSchemaID(TilesetEditData * tileset) { TilesetMetadataSchemaID identifierMax = 0; for (unsigned int schemaIndex = 0; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { if (tileset->metadataSchemas[schemaIndex].identifier > identifierMax) { identifierMax = tileset->metadataSchemas[schemaIndex].identifier; } } return identifierMax + 1; } void TilesetEditData_addMetadataSchema(TilesetEditData * tileset, TilesetMetadataSchema schema) { TilesetEditData_insertMetadataSchema(tileset, tileset->metadataSchemaCount, schema); } void TilesetEditData_insertMetadataSchema(TilesetEditData * tileset, unsigned int schemaIndex, TilesetMetadataSchema schema) { assert(schemaIndex <= tileset->metadataSchemaCount); tileset->metadataSchemas = realloc(tileset->metadataSchemas, (tileset->metadataSchemaCount + 1) * sizeof(*tileset->metadataSchemas)); for (unsigned int schemaIndex2 = tileset->metadataSchemaCount; schemaIndex2 > schemaIndex; schemaIndex2--) { tileset->metadataSchemas[schemaIndex2] = tileset->metadataSchemas[schemaIndex2 - 1]; } tileset->metadataSchemas[schemaIndex] = schema; tileset->metadataSchemaCount++; } DataValueSchema * TilesetEditData_getMetadataSchema(TilesetEditData * tileset, TilesetMetadataSchemaID schemaID) { for (unsigned int schemaIndex = 0; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { if (tileset->metadataSchemas[schemaIndex].identifier == schemaID) { return tileset->metadataSchemas[schemaIndex].schema; } } return NULL; } TilesetMetadataSchema * TilesetEditData_getMetadataSchemaInfo(TilesetEditData * tileset, TilesetMetadataSchemaID schemaID) { for (unsigned int schemaIndex = 0; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { if (tileset->metadataSchemas[schemaIndex].identifier == schemaID) { return &tileset->metadataSchemas[schemaIndex]; } } return NULL; } TilesetMetadataSchema * TilesetEditData_getMetadataSchemaInfoForName(TilesetEditData * tileset, const char * name) { for (unsigned int schemaIndex = 0; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { if (!strcmp(tileset->metadataSchemas[schemaIndex].name, name)) { return &tileset->metadataSchemas[schemaIndex]; } } return NULL; } void TilesetEditData_removeMetadataSchemaAtIndex(TilesetEditData * tileset, unsigned int schemaIndex) { if (schemaIndex < tileset->metadataSchemaCount) { free(tileset->metadataSchemas[schemaIndex].name); DataValueSchema_dispose(tileset->metadataSchemas[schemaIndex].schema); tileset->metadataSchemaCount--; for (; schemaIndex < tileset->metadataSchemaCount; schemaIndex++) { tileset->metadataSchemas[schemaIndex] = tileset->metadataSchemas[schemaIndex + 1]; } } } void TilesetEditData_reorderMetadataSchema(TilesetEditData * tileset, unsigned int fromSchemaIndex, unsigned int toSchemaIndex) { assert(fromSchemaIndex < tileset->metadataSchemaCount); assert(toSchemaIndex < tileset->metadataSchemaCount); TilesetMetadataSchema swap = tileset->metadataSchemas[fromSchemaIndex]; int direction = (toSchemaIndex > fromSchemaIndex) * 2 - 1; for (unsigned int swapIndex = fromSchemaIndex; swapIndex != toSchemaIndex; swapIndex += direction) { tileset->metadataSchemas[swapIndex] = tileset->metadataSchemas[swapIndex + direction]; } tileset->metadataSchemas[toSchemaIndex] = swap; } static const char * getSchemaNameAtIndex(unsigned int index, void * context) { TilesetEditData * tileset = context; return tileset->metadataSchemas[index].name; } TilesetMetadataSchema TilesetEditData_newMetadataSchema(TilesetEditData * tileset, const char * defaultName) { TilesetMetadataSchema schema; schema.identifier = TilesetEditData_getUnusedMetadataSchemaID(tileset); if (defaultName == NULL) { defaultName = "schema"; } schema.name = newDisplayStringWithNumberSuffix(defaultName, 32, tileset->metadataSchemaCount, getSchemaNameAtIndex, tileset); schema.schema = DataValueSchema_create(0, NULL); return schema; } unsigned int getMetadataSizeForSchemaFieldType(TilesetSchemaFieldType type) { switch (type) { case METADATA_FIELD_TYPE_INTEGER: return 4; case METADATA_FIELD_TYPE_STRING: case METADATA_FIELD_TYPE_TEXT: return 1; case METADATA_FIELD_TYPE_ENUMERATION: return 8; case METADATA_FIELD_TYPE_COORDINATE: case METADATA_FIELD_TYPE_OFFSET: return 4; case METADATA_FIELD_TYPE_SLIDER: return 8; case METADATA_FIELD_TYPE_BOOLEAN: case METADATA_FIELD_TYPE_FLOAT: case METADATA_FIELD_TYPE_FIXED: case METADATA_FIELD_TYPE_ARRAY: return 0; } return 0; } TilesetSchemaFieldType getMetadataFieldType(DataValueSchemaField * field) { switch (field->type) { case DATA_TYPE_BOOLEAN: return METADATA_FIELD_TYPE_BOOLEAN; case DATA_TYPE_INT8: case DATA_TYPE_UINT8: case DATA_TYPE_INT16: case DATA_TYPE_UINT16: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: case DATA_TYPE_INT64: case DATA_TYPE_UINT64: switch (getSchemaIntegerType(field->metadata)) { case SCHEMA_INTEGER_RAW: return METADATA_FIELD_TYPE_INTEGER; case SCHEMA_INTEGER_ENUMERATION: return METADATA_FIELD_TYPE_ENUMERATION; case SCHEMA_INTEGER_COORDINATE: return METADATA_FIELD_TYPE_COORDINATE; case SCHEMA_INTEGER_SLIDER: return METADATA_FIELD_TYPE_SLIDER; case SCHEMA_INTEGER_OFFSET: return METADATA_FIELD_TYPE_OFFSET; } break; case DATA_TYPE_FIXED_16_16: return METADATA_FIELD_TYPE_FIXED; case DATA_TYPE_FLOAT: return METADATA_FIELD_TYPE_FLOAT; case DATA_TYPE_STRING: if (getSchemaStringIsMultiline(field->metadata)) { return METADATA_FIELD_TYPE_TEXT; } return METADATA_FIELD_TYPE_STRING; case DATA_TYPE_ARRAY: return METADATA_FIELD_TYPE_ARRAY; case DATA_TYPE_DOUBLE: case DATA_TYPE_BLOB: case DATA_TYPE_POINTER: case DATA_TYPE_HASH_TABLE: case DATA_TYPE_ASSOCIATIVE_ARRAY: // Not available break; } return METADATA_FIELD_TYPE_BOOLEAN; } TilesetSchemaIntegerType getSchemaIntegerType(const void * metadata) { return *(TilesetSchemaIntegerType *) metadata; } void setSchemaIntegerType(void * metadata, TilesetSchemaIntegerType type) { *(TilesetSchemaIntegerType *) metadata = type; } TilesetEnumerationID getSchemaIntegerEnumerationID(const void * metadata) { return *(TilesetEnumerationID *) (metadata + 4); } void setSchemaIntegerEnumerationID(void * metadata, TilesetEnumerationID enumerationID) { *(TilesetEnumerationID *) (metadata + 4) = enumerationID; } int16_t getSchemaIntegerRangeMin(const void * metadata) { return *(int16_t *) (metadata + 4); } int16_t getSchemaIntegerRangeMax(const void * metadata) { return *(int16_t *) (metadata + 6); } void setSchemaIntegerRangeMin(void * metadata, int16_t rangeMin) { *(int16_t *) (metadata + 4) = rangeMin; } void setSchemaIntegerRangeMax(void * metadata, int16_t rangeMax) { *(int16_t *) (metadata + 6) = rangeMax; } bool getSchemaStringIsMultiline(const void * metadata) { return *(bool *) metadata; } void setSchemaStringIsMultiline(void * metadata, bool isMultiline) { *(bool *) metadata = isMultiline; } union packedCoordinateValue { uint32_t uint32; struct { int16_t x; int16_t y; } pair; }; int16_t getMetadataValueCoordinateX(uint32_t value) { union packedCoordinateValue coordinateValue = {.uint32 = value}; return coordinateValue.pair.x; } int16_t getMetadataValueCoordinateY(uint32_t value) { union packedCoordinateValue coordinateValue = {.uint32 = value}; return coordinateValue.pair.y; } void setMetadataValueCoordinateX(uint32_t * value, int16_t x) { union packedCoordinateValue coordinateValue = {.uint32 = *value}; coordinateValue.pair.x = x; *value = coordinateValue.uint32; } void setMetadataValueCoordinateY(uint32_t * value, int16_t y) { union packedCoordinateValue coordinateValue = {.uint32 = *value}; coordinateValue.pair.y = y; *value = coordinateValue.uint32; }