| 324 | } |
| 325 | |
| 326 | bool Validate() const |
| 327 | { |
| 328 | #define Check(Test, ErrorMessage, ...) \ |
| 329 | do \ |
| 330 | { \ |
| 331 | if(!(Test)) \ |
| 332 | { \ |
| 333 | log_error("datafile", "invalid file information: " ErrorMessage, ##__VA_ARGS__); \ |
| 334 | return false; \ |
| 335 | } \ |
| 336 | } while(false) |
| 337 | |
| 338 | // validate item types |
| 339 | int64_t CountedItems = 0; |
| 340 | std::unordered_set<int> UsedItemTypes; |
| 341 | for(int Index = 0; Index < m_Header.m_NumItemTypes; Index++) |
| 342 | { |
| 343 | const CDatafileItemType &ItemType = m_Info.m_pItemTypes[Index]; |
| 344 | Check(ItemType.m_Type >= 0 && ItemType.m_Type <= MAX_ITEM_TYPE, "item type has invalid type. index=%d type=%d", Index, ItemType.m_Type); |
| 345 | const auto [_, Inserted] = UsedItemTypes.insert(ItemType.m_Type); |
| 346 | Check(Inserted, "item type has duplicate type. index=%d type=%d", Index, ItemType.m_Type); |
| 347 | Check(ItemType.m_Num > 0, "item type has invalid number of items. index=%d type=%d num=%d", Index, ItemType.m_Type, ItemType.m_Num); |
| 348 | Check(ItemType.m_Start == CountedItems, "item type has invalid start. index=%d type=%d start=%d", Index, ItemType.m_Type, ItemType.m_Start); |
| 349 | CountedItems += ItemType.m_Num; |
| 350 | if(CountedItems > m_Header.m_NumItems) |
| 351 | { |
| 352 | break; |
| 353 | } |
| 354 | } |
| 355 | Check(CountedItems == m_Header.m_NumItems, "mismatched number of items in item types. counted=%" PRId64 " header=%d", CountedItems, m_Header.m_NumItems); |
| 356 | |
| 357 | // validate item offsets |
| 358 | int PrevItemOffset = -1; |
| 359 | for(int Index = 0; Index < m_Header.m_NumItems; Index++) |
| 360 | { |
| 361 | const int Offset = m_Info.m_pItemOffsets[Index]; |
| 362 | if(Index == 0) |
| 363 | { |
| 364 | Check(Offset == 0, "first item offset is not zero. offset=%d", Offset); |
| 365 | } |
| 366 | else |
| 367 | { |
| 368 | Check(Offset > PrevItemOffset, "item offset not greater than previous. index=%d offset=%d previous=%d", Index, Offset, PrevItemOffset); |
| 369 | } |
| 370 | Check(Offset < m_Header.m_ItemSize, "item offset larger than total item size. index=%d offset=%d total=%d", Index, Offset, m_Header.m_ItemSize); |
| 371 | PrevItemOffset = Offset; |
| 372 | } |
| 373 | |
| 374 | // validate item sizes, types and IDs |
| 375 | int64_t TotalItemSize = 0; |
| 376 | for(int TypeIndex = 0; TypeIndex < m_Header.m_NumItemTypes; TypeIndex++) |
| 377 | { |
| 378 | std::unordered_set<int> UsedItemIds; |
| 379 | const CDatafileItemType &ItemType = m_Info.m_pItemTypes[TypeIndex]; |
| 380 | for(int ItemIndex = ItemType.m_Start; ItemIndex < ItemType.m_Start + ItemType.m_Num; ItemIndex++) |
| 381 | { |
| 382 | const int FileItemSize = GetFileItemSize(ItemIndex); |
| 383 | Check(FileItemSize >= (int)sizeof(CDatafileItem), "map item too small for header. type_index=%d item_index=%d size=%d header=%d", TypeIndex, ItemIndex, FileItemSize, (int)sizeof(CDatafileItem)); |