From 6b30ac37b30f46c2df381ad29ae7c0a646c87c3b Mon Sep 17 00:00:00 2001 From: Xottab-DUTY Date: Tue, 25 Jun 2019 15:59:15 +0500 Subject: [PATCH] Support reading SOC game.graph and level.gct (#392) --- src/Common/LevelStructure.hpp | 27 +++++++- src/xrAICore/Navigation/game_graph.h | 2 + src/xrAICore/Navigation/game_graph_inline.h | 42 ++++++++---- .../Navigation/game_level_cross_table.h | 4 -- .../game_level_cross_table_inline.h | 13 ++-- src/xrAICore/Navigation/level_graph.cpp | 13 ++-- src/xrAICore/Navigation/level_graph.h | 7 +- src/xrAICore/Navigation/level_graph_inline.h | 24 +++---- src/xrAICore/Navigation/level_graph_manager.h | 65 +++++++++++++++++++ src/xrAICore/xrAICore.vcxproj | 1 + src/xrAICore/xrAICore.vcxproj.filters | 3 + src/xrGame/alife_spawn_registry.cpp | 6 ++ src/xrGame/alife_spawn_registry_header.cpp | 2 +- 13 files changed, 163 insertions(+), 46 deletions(-) create mode 100644 src/xrAICore/Navigation/level_graph_manager.h diff --git a/src/Common/LevelStructure.hpp b/src/Common/LevelStructure.hpp index 1bca4325270..d954bd8f58b 100644 --- a/src/Common/LevelStructure.hpp +++ b/src/Common/LevelStructure.hpp @@ -169,6 +169,15 @@ struct NodeCompressed friend class CNodeRenumberer; friend class CRenumbererConverter; }; + +struct NodeCompressedOld +{ + u8 data[12]; + NodeCompressed::SCover cover; + u16 plane; + NodePosition p; +}; + #endif #ifdef AI_COMPILER @@ -263,10 +272,24 @@ struct SNodePositionOld typedef SNodePositionOld NodePosition; #endif -const char LEVEL_GRAPH_NAME[] = "level.ai"; +constexpr cpcstr LEVEL_GRAPH_NAME = "level.ai"; const u32 XRCL_CURRENT_VERSION = 18; // input const u32 XRCL_PRODUCTION_VERSION = 14; // output const u32 CFORM_CURRENT_VERSION = 4; const u32 MAX_NODE_BIT_COUNT = 23; -const u32 XRAI_CURRENT_VERSION = 10; + +enum xrAI_Versions +{ + XRAI_VERSION_SOC = 8, + XRAI_VERSION_CS = 9, + XRAI_VERSION_COP = 10, + + XRAI_VERSION_ALLOWED = XRAI_VERSION_SOC, + XRAI_VERSION_OPENXRAY = XRAI_VERSION_COP, + + XRAI_CURRENT_VERSION = XRAI_VERSION_OPENXRAY +}; + +#define ASSERT_XRAI_VERSION_MATCH(version, description)\ + R_ASSERT2((version) >= XRAI_VERSION_ALLOWED && (version) <= XRAI_CURRENT_VERSION, description); diff --git a/src/xrAICore/Navigation/game_graph.h b/src/xrAICore/Navigation/game_graph.h index 668dc073733..11bcc240312 100644 --- a/src/xrAICore/Navigation/game_graph.h +++ b/src/xrAICore/Navigation/game_graph.h @@ -11,6 +11,8 @@ #include "xrAICore/Navigation/game_graph_space.h" #include "xrAICore/Navigation/game_level_cross_table.h" +constexpr cpcstr GRAPH_NAME = "game.graph"; + class CGameGraph { private: diff --git a/src/xrAICore/Navigation/game_graph_inline.h b/src/xrAICore/Navigation/game_graph_inline.h index c1266cb3964..337b8736565 100644 --- a/src/xrAICore/Navigation/game_graph_inline.h +++ b/src/xrAICore/Navigation/game_graph_inline.h @@ -13,14 +13,22 @@ inline void CGameGraph::Initialize(IReader& stream, bool own) ownReader = own; m_reader = &stream; m_header.load(m_reader); - R_ASSERT2(header().version() == XRAI_CURRENT_VERSION, "Graph version mismatch!"); + ASSERT_XRAI_VERSION_MATCH(header().version(), "Game graph version mismatch!"); m_nodes = (CVertex*)m_reader->pointer(); m_current_level_some_vertex_id = _GRAPH_ID(-1); m_enabled.assign(header().vertex_count(), true); + + if (header().version() <= XRAI_VERSION_SOC) + { + m_cross_tables = nullptr; + m_current_level_cross_table = nullptr; + return; + } + u8* temp = (u8*)(m_nodes + header().vertex_count()); temp += header().edge_count() * sizeof(CGameGraph::CEdge); m_cross_tables = (u32*)(((CLevelPoint*)temp) + header().death_point_count()); - m_current_level_cross_table = 0; + m_current_level_cross_table = nullptr; } IC CGameGraph::CGameGraph(LPCSTR file_name, u32 current_version) @@ -250,21 +258,29 @@ IC void GameGraph::CHeader::save(IWriter* writer) IC void CGameGraph::set_current_level(u32 const level_id) { xr_delete(m_current_level_cross_table); - u32* current_cross_table = m_cross_tables; - GameGraph::LEVEL_MAP::const_iterator I = header().levels().begin(); - GameGraph::LEVEL_MAP::const_iterator E = header().levels().end(); - for (; I != E; ++I) + if (m_cross_tables) { - if (level_id != (*I).first) + u32* current_cross_table = m_cross_tables; + GameGraph::LEVEL_MAP::const_iterator I = header().levels().begin(); + GameGraph::LEVEL_MAP::const_iterator E = header().levels().end(); + for (; I != E; ++I) { - current_cross_table = (u32*)((u8*)current_cross_table + *current_cross_table); - continue; + if (level_id != (*I).first) + { + current_cross_table = (u32*)((u8*)current_cross_table + *current_cross_table); + continue; + } + + m_current_level_cross_table = new CGameLevelCrossTable(current_cross_table + 1, *current_cross_table); + break; } - - m_current_level_cross_table = new CGameLevelCrossTable(current_cross_table + 1, *current_cross_table); - break; } - + else + { + string_path fName; + FS.update_path(fName, "$level$", CROSS_TABLE_NAME); + m_current_level_cross_table = new CGameLevelCrossTable(fName); + } VERIFY(m_current_level_cross_table); m_current_level_some_vertex_id = _GRAPH_ID(-1); diff --git a/src/xrAICore/Navigation/game_level_cross_table.h b/src/xrAICore/Navigation/game_level_cross_table.h index b1c8ebad07e..db44e2bdc7e 100644 --- a/src/xrAICore/Navigation/game_level_cross_table.h +++ b/src/xrAICore/Navigation/game_level_cross_table.h @@ -71,17 +71,13 @@ class CGameLevelCrossTable CHeader m_tCrossTableHeader; CCell* m_tpaCrossTable; -#ifdef AI_COMPILER private: IReader* m_tpCrossTableVFS; IReader* m_chunk; -#endif // AI_COMPILER public: IC CGameLevelCrossTable(const void* buffer, const u32& buffer_size); -#ifdef AI_COMPILER IC CGameLevelCrossTable(LPCSTR fName); -#endif // AI_COMPILER public: IC virtual ~CGameLevelCrossTable(); diff --git a/src/xrAICore/Navigation/game_level_cross_table_inline.h b/src/xrAICore/Navigation/game_level_cross_table_inline.h index 393e1ee2c21..3b791a497f1 100644 --- a/src/xrAICore/Navigation/game_level_cross_table_inline.h +++ b/src/xrAICore/Navigation/game_level_cross_table_inline.h @@ -8,7 +8,6 @@ #pragma once -#ifdef AI_COMPILER IC CGameLevelCrossTable::CGameLevelCrossTable(LPCSTR fName) { m_tpCrossTableVFS = FS.r_open(fName); @@ -19,25 +18,29 @@ IC CGameLevelCrossTable::CGameLevelCrossTable(LPCSTR fName) chunk->r(&m_tCrossTableHeader, sizeof(m_tCrossTableHeader)); chunk->close(); - R_ASSERT2(m_tCrossTableHeader.version() == XRAI_CURRENT_VERSION, "Cross table version mismatch!"); + ASSERT_XRAI_VERSION_MATCH(m_tCrossTableHeader.version(), "Cross table version mismatch!"); m_chunk = m_tpCrossTableVFS->open_chunk(CROSS_TABLE_CHUNK_DATA); R_ASSERT2(m_chunk, "Cross table is corrupted!"); m_tpaCrossTable = (CCell*)m_chunk->pointer(); } -#endif // AI_COMPILER IC CGameLevelCrossTable::CGameLevelCrossTable(const void* buffer, const u32& /*buffer_size*/) { memcpy(&m_tCrossTableHeader, buffer, sizeof(m_tCrossTableHeader)); buffer = (const u8*)buffer + sizeof(m_tCrossTableHeader); - R_ASSERT2(m_tCrossTableHeader.version() == XRAI_CURRENT_VERSION, "Cross table version mismatch!"); + ASSERT_XRAI_VERSION_MATCH(m_tCrossTableHeader.version(), "Cross table version mismatch!"); m_tpaCrossTable = (CCell*)buffer; } -IC CGameLevelCrossTable::~CGameLevelCrossTable(){}; +IC CGameLevelCrossTable::~CGameLevelCrossTable() +{ + if (m_chunk) + m_chunk->close(); + FS.r_close(m_tpCrossTableVFS); +} IC const CGameLevelCrossTable::CCell& CGameLevelCrossTable::vertex(u32 level_vertex_id) const { diff --git a/src/xrAICore/Navigation/level_graph.cpp b/src/xrAICore/Navigation/level_graph.cpp index 847bcca37fd..9a2b9d512ae 100644 --- a/src/xrAICore/Navigation/level_graph.cpp +++ b/src/xrAICore/Navigation/level_graph.cpp @@ -26,14 +26,13 @@ CLevelGraph::CLevelGraph() void CLevelGraph::Initialize(const char* filePath) { - m_reader = FS.r_open(filePath); // m_header & data m_header = (CHeader*)m_reader->pointer(); - R_ASSERT(header().version() == XRAI_CURRENT_VERSION); + ASSERT_XRAI_VERSION_MATCH(header().version(), "Level graph version mismatch"); m_reader->advance(sizeof(CHeader)); const auto& box = header().box(); - m_nodes = (CVertex*)m_reader->pointer(); + m_nodes = new CLevelGraphManager(m_reader, header().vertex_count(), header().version()); m_row_length = iFloor((box.vMax.z - box.vMin.z) / header().cell_size() + EPS_L + 1.5f); m_column_length = iFloor((box.vMax.x - box.vMin.x) / header().cell_size() + EPS_L + 1.5f); m_access_mask.assign(header().vertex_count(), true); @@ -198,8 +197,8 @@ u32 CLevelGraph::vertex_id(const Fvector& position) const make_string("invalid position for CLevelGraph::vertex_id specified: [%f][%f][%f]", VPUSH(position))); CPosition _vertex_position = vertex_position(position); - CVertex* B = m_nodes; - CVertex* E = m_nodes + header().vertex_count(); + CVertex* B = m_nodes->begin(); + CVertex* E = m_nodes->end(); CVertex* I = std::lower_bound(B, E, _vertex_position.xz()); if ((I == E) || ((*I).position().xz() != _vertex_position.xz())) return (u32(-1)); @@ -269,8 +268,8 @@ u32 CLevelGraph::guess_vertex_id(u32 const& current_vertex_id, Fvector const& po float result_distance = nearest(best_point, position, vertex_contour); u32 result_vertex_id = current_vertex_id; - CVertex const* B = m_nodes; - CVertex const* E = m_nodes + header().vertex_count(); + CVertex const* B = m_nodes->begin(); + CVertex const* E = m_nodes->end(); u32 start_x = (u32)std::max(0, int(x) - max_guess_vertex_count); u32 stop_x = std::min(max_x(), x + (u32)max_guess_vertex_count); u32 start_z = (u32)std::max(0, int(z) - max_guess_vertex_count); diff --git a/src/xrAICore/Navigation/level_graph.h b/src/xrAICore/Navigation/level_graph.h index 9f4c3a6e885..8d33b6b5eee 100644 --- a/src/xrAICore/Navigation/level_graph.h +++ b/src/xrAICore/Navigation/level_graph.h @@ -11,6 +11,7 @@ #include "Common/LevelStructure.hpp" #include "xrAICore/Navigation/level_graph_space.h" #include "xrAICore/Navigation/game_graph_space.h" +#include "xrAICore/Navigation/level_graph_manager.h" namespace LevelGraph { @@ -43,10 +44,12 @@ class XRAICORE_API CLevelGraph typedef LevelGraph::SContour SContour; typedef LevelGraph::ELineIntersections ELineIntersections; + using CLevelGraphManager = LevelGraph::CLevelGraphManager; + private: IReader* m_reader; // level graph virtual storage CHeader* m_header; // level graph header - CVertex* m_nodes; // nodes array + CLevelGraphManager* m_nodes; // contains nodes array xr_vector m_access_mask; GameGraph::_LEVEL_ID m_level_id; // unique level identifier u32 m_row_length; @@ -240,7 +243,7 @@ class XRAICORE_API CLevelGraph IC bool valid_vertex_position(const Fvector& position) const; bool neighbour_in_direction(const Fvector& direction, u32 start_vertex_id) const; - IC CVertex* vertices() { return m_nodes; } + IC CVertex* vertices() { return m_nodes->begin(); } }; IC bool operator<(const CLevelGraph::CVertex& vertex, const u32& vertex_xz); diff --git a/src/xrAICore/Navigation/level_graph_inline.h b/src/xrAICore/Navigation/level_graph_inline.h index 19749d4a560..4328527d0dc 100644 --- a/src/xrAICore/Navigation/level_graph_inline.h +++ b/src/xrAICore/Navigation/level_graph_inline.h @@ -9,8 +9,8 @@ #pragma once #include "xrCore/_fbox2.h" -IC CLevelGraph::const_vertex_iterator CLevelGraph::begin() const { return (m_nodes); } -IC CLevelGraph::const_vertex_iterator CLevelGraph::end() const { return (m_nodes + header().vertex_count()); } +IC CLevelGraph::const_vertex_iterator CLevelGraph::begin() const { return (m_nodes->begin()); } +IC CLevelGraph::const_vertex_iterator CLevelGraph::end() const { return (m_nodes->end()); } IC const CLevelGraph::CHeader& CLevelGraph::header() const { return (*m_header); } ICF bool CLevelGraph::valid_vertex_id(u32 id) const { @@ -21,13 +21,13 @@ ICF bool CLevelGraph::valid_vertex_id(u32 id) const ICF CLevelGraph::CVertex* CLevelGraph::vertex(const u32 vertex_id) const { VERIFY(valid_vertex_id(vertex_id)); - return (m_nodes + vertex_id); + return m_nodes->at(vertex_id); } ICF u32 CLevelGraph::vertex(const CVertex* vertex_p) const { - VERIFY((vertex_p >= m_nodes) && valid_vertex_id(u32(vertex_p - m_nodes))); - return (u32(vertex_p - m_nodes)); + VERIFY((vertex_p >= m_nodes->begin()) && valid_vertex_id(u32(vertex_p - m_nodes->begin()))); + return (u32(vertex_p - m_nodes->begin())); } ICF u32 CLevelGraph::vertex(const CVertex& vertex_r) const { return (vertex(&vertex_r)); } @@ -293,8 +293,8 @@ IC void CLevelGraph::set_invalid_vertex(u32& vertex_id, CVertex** vertex) const IC const u32 CLevelGraph::vertex_id(const CLevelGraph::CVertex* vertex) const { - VERIFY(valid_vertex_id(u32(vertex - m_nodes))); - return (u32(vertex - m_nodes)); + VERIFY(valid_vertex_id(u32(vertex - m_nodes->begin()))); + return (u32(vertex - m_nodes->begin())); } IC Fvector CLevelGraph::v3d(const Fvector2& vector2d) const { return (Fvector().set(vector2d.x, 0.f, vector2d.y)); } @@ -560,19 +560,19 @@ IC void CLevelGraph::iterate_vertices( if (valid_vertex_position(min_position)) I = std::lower_bound( - m_nodes, m_nodes + header().vertex_count(), vertex_position(min_position).xz(), &vertex::predicate2); + m_nodes->begin(), m_nodes->end(), vertex_position(min_position).xz(), &vertex::predicate2); else - I = m_nodes; + I = m_nodes->begin(); if (valid_vertex_position(max_position)) { E = std::upper_bound( - m_nodes, m_nodes + header().vertex_count(), vertex_position(max_position).xz(), &vertex::predicate); - if (E != (m_nodes + header().vertex_count())) + m_nodes->begin(), m_nodes->end(), vertex_position(max_position).xz(), &vertex::predicate); + if (E != (m_nodes->end())) ++E; } else - E = m_nodes + header().vertex_count(); + E = m_nodes->end(); for (; I != E; ++I) predicate(*I); diff --git a/src/xrAICore/Navigation/level_graph_manager.h b/src/xrAICore/Navigation/level_graph_manager.h new file mode 100644 index 00000000000..e1f63e43347 --- /dev/null +++ b/src/xrAICore/Navigation/level_graph_manager.h @@ -0,0 +1,65 @@ +#pragma once + +#include "xrAICore/Navigation/level_graph_space.h" + +namespace LevelGraph +{ +class CLevelGraphManager +{ + bool compatibilityMode; + xr_vector m_nodes; // nodes array + +public: + CLevelGraphManager(IReader* stream, u32 vertex_count, u32 version) + { + m_nodes.resize(vertex_count); + if (version <= 8) + { + compatibilityMode = true; + NodeCompressedOld* nodes = static_cast(stream->pointer()); + for (u32 i = 0; i < vertex_count; ++i) + { + CVertex* vertex = new CVertex(); + + NodeCompressed& newNode = *vertex; + NodeCompressedOld& oldNode = nodes[i]; + CopyMemory(newNode.data, oldNode.data, sizeof(oldNode.data) / sizeof(u8)); + newNode.high = oldNode.cover; + newNode.low = oldNode.cover; + newNode.plane = oldNode.plane; + newNode.p = oldNode.p; + + m_nodes[i] = vertex; + } + } + else + { + compatibilityMode = false; + CVertex* begin = static_cast(stream->pointer()); + CVertex* end = begin + vertex_count; + for (size_t i = 0; begin != end; ++begin, ++i) + { + m_nodes[i] = begin; + } + } + } + + ~CLevelGraphManager() + { + if (compatibilityMode) + { + for (auto& node : m_nodes) + xr_delete(node); + } + m_nodes.clear(); + } + + CVertex* begin() { return m_nodes.front(); } + CVertex* end() { return m_nodes.back(); } + + CVertex* at(size_t id) { return m_nodes[id]; } + CVertex* operator[](size_t id) { return m_nodes[id]; } + + CVertex* operator+(size_t id) { return m_nodes[id]; } +}; +} // namespace LevelGraph diff --git a/src/xrAICore/xrAICore.vcxproj b/src/xrAICore/xrAICore.vcxproj index fe973d4a04e..72f28a0f493 100644 --- a/src/xrAICore/xrAICore.vcxproj +++ b/src/xrAICore/xrAICore.vcxproj @@ -195,6 +195,7 @@ + diff --git a/src/xrAICore/xrAICore.vcxproj.filters b/src/xrAICore/xrAICore.vcxproj.filters index 2adf737ae80..1c4a89a1962 100644 --- a/src/xrAICore/xrAICore.vcxproj.filters +++ b/src/xrAICore/xrAICore.vcxproj.filters @@ -369,6 +369,9 @@ Kernel + + AI\Navigation\LevelGraph + diff --git a/src/xrGame/alife_spawn_registry.cpp b/src/xrGame/alife_spawn_registry.cpp index 31042b962d6..ab0243c67ba 100644 --- a/src/xrGame/alife_spawn_registry.cpp +++ b/src/xrGame/alife_spawn_registry.cpp @@ -141,6 +141,12 @@ void CALifeSpawnRegistry::load(IReader& file_stream, xrGUID* save_guid) VERIFY(!m_chunk); m_chunk = file_stream.open_chunk(4); + if (!m_chunk) + { + string_path file_name; + FS.update_path(file_name, "$game_data$", GRAPH_NAME); + m_chunk = FS.r_open(file_name); + } R_ASSERT2(m_chunk, "Spawn version mismatch - REBUILD SPAWN!"); VERIFY(!m_game_graph); diff --git a/src/xrGame/alife_spawn_registry_header.cpp b/src/xrGame/alife_spawn_registry_header.cpp index f96666e5b8b..a8797107e00 100644 --- a/src/xrGame/alife_spawn_registry_header.cpp +++ b/src/xrGame/alife_spawn_registry_header.cpp @@ -15,7 +15,7 @@ CALifeSpawnHeader::~CALifeSpawnHeader() {} void CALifeSpawnHeader::load(IReader& file_stream) { m_version = file_stream.r_u32(); - R_ASSERT2(XRAI_CURRENT_VERSION == m_version, "'game.spawn' version mismatch!"); + ASSERT_XRAI_VERSION_MATCH(XRAI_CURRENT_VERSION, "'game.spawn' version mismatch!"); file_stream.r(&m_guid, sizeof(m_guid)); file_stream.r(&m_graph_guid, sizeof(m_graph_guid)); m_count = file_stream.r_u32();