validate.c (3348B)
1 #include "game/content.h" 2 #include "engine/validate.h" 3 4 #include <math.h> 5 #include <stdio.h> 6 7 #define FAIL(...) do { \ 8 if (error && size) snprintf(error, size, __VA_ARGS__); \ 9 return -1; \ 10 } while (0) 11 12 static int 13 ph_asset_valid(int id) 14 { 15 return id >= 0 && id < PH_ASSET_COUNT && 16 (id == PH_ASSET_NONE || (ph_asset_defs[id].path && *ph_asset_defs[id].path)); 17 } 18 19 int 20 ph_game_validate_content(char *error, size_t size) 21 { 22 PhWorld lookup = { .content = ph_game_world.content }; 23 int i, j; 24 25 if (ph_validate_world(&ph_game_world, error, size) < 0 || 26 ph_validate_spells(&ph_player_spellbook, error, size) < 0) return -1; 27 for (i = 1; i < PH_AUDIO_COUNT; ++i) 28 if (!ph_audio_defs[i].path || !*ph_audio_defs[i].path || 29 !isfinite(ph_audio_defs[i].gain) || ph_audio_defs[i].gain < 0) 30 FAIL("audio %d: invalid path or gain", i); 31 for (i = 0; i < PH_TILE_DEF_COUNT; ++i) { 32 const PhTileDef *tile = &ph_tile_defs[i]; 33 34 if (!ph_asset_valid(tile->asset_id) || !ph_asset_valid(tile->background_asset_id) || 35 tile->sprite_tile_x < 0 || tile->sprite_tile_y < 0 || 36 tile->background_sprite_tile_x < 0 || tile->background_sprite_tile_y < 0) 37 FAIL("tile '%c': invalid sprite reference", tile->symbol); 38 } 39 for (i = 0; i < PH_ITEM_DEF_COUNT; ++i) 40 if (!ph_asset_valid(ph_item_defs[i].asset_id)) 41 FAIL("item %d: invalid asset %d", ph_item_defs[i].id, ph_item_defs[i].asset_id); 42 for (i = 0; i < PH_ENTITY_DEF_COUNT; ++i) { 43 const PhEntityDef *entity = &ph_entity_defs[i]; 44 45 if (!ph_asset_valid(entity->asset_id)) FAIL("entity %d: invalid asset %d", entity->id, entity->asset_id); 46 if (entity->interaction_id && !ph_game_interaction_def(entity->interaction_id)) 47 FAIL("entity %d: unknown interaction %d", entity->id, entity->interaction_id); 48 } 49 for (i = 0; i < PH_INTERACTION_COUNT; ++i) { 50 const PhInteractionDef *interaction = &ph_interaction_defs[i]; 51 52 if (interaction->id <= 0 || interaction->kind != PH_INTERACTION_SHOP || 53 !ph_game_shop_def(interaction->content_id)) 54 FAIL("interaction %d: invalid kind or shop %d", interaction->id, interaction->content_id); 55 for (j = 0; j < i; ++j) 56 if (ph_interaction_defs[j].id == interaction->id) FAIL("interaction %d: duplicate ID", interaction->id); 57 } 58 for (i = 0; i < PH_SHOP_COUNT; ++i) { 59 const PhShopDef *shop = &ph_shop_defs[i]; 60 61 if (shop->id <= 0 || !shop->name || shop->kind < PH_SHOP_ITEMS || 62 shop->kind > PH_SHOP_SPELLS || shop->stock_count < 0 || shop->stock_count > PH_SHOP_STOCK_MAX) 63 FAIL("shop %d: invalid definition", shop->id); 64 for (j = 0; j < i; ++j) 65 if (ph_shop_defs[j].id == shop->id) FAIL("shop %d: duplicate ID", shop->id); 66 for (j = 0; j < PH_SHOP_TALK_LINES; ++j) 67 if (!shop->talk[j]) FAIL("shop %d: missing talk line %d", shop->id, j); 68 for (j = 0; j < shop->stock_count; ++j) { 69 const PhItemDef *item = ph_world_item_def(&lookup, shop->stock[j]); 70 const PhSpellDef *spell = ph_spellbook_def(&ph_player_spellbook, shop->stock[j]); 71 int k; 72 73 if (shop->kind == PH_SHOP_ITEMS ? (!item || item->buy_value <= 0) : (!spell || spell->buy_value <= 0)) 74 FAIL("shop %d stock entry %d: invalid %s %d", shop->id, j, 75 shop->kind == PH_SHOP_ITEMS ? "item" : "spell", shop->stock[j]); 76 for (k = 0; k < j; ++k) 77 if (shop->stock[k] == shop->stock[j]) FAIL("shop %d: duplicate stock ID %d", shop->id, shop->stock[j]); 78 } 79 } 80 return 0; 81 }