MapFileSerializer.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. * Copyright (C) 2022 The V-Gears Team
  3. *
  4. * This file is part of V-Gears
  5. *
  6. * V-Gears is free software: you can redistribute it and/or modify it under
  7. * terms of the GNU General Public License as published by the Free Software
  8. * Foundation, version 3.0 (GPLv3) of the License.
  9. *
  10. * V-Gears is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <boost/test/unit_test.hpp>
  16. #include <algorithm>
  17. #include <vector>
  18. #include "data/worldmap/WorldmapFile.h"
  19. #include "../../../src/data/worldmap/WorldmapFileSerializer.h"
  20. namespace {
  21. const size_t WORLD_MAP_BLOCK_SIZE = 0xB800;
  22. class TestWorldmapFile : public VGears::WorldmapFile{
  23. public:
  24. TestWorldmapFile() : VGears::WorldmapFile(nullptr, "", 0, "") {}
  25. };
  26. void AppendUInt16(std::vector<VGears::uint8>& data, VGears::uint16 value){
  27. data.push_back(static_cast<VGears::uint8>(value & 0xFF));
  28. data.push_back(static_cast<VGears::uint8>(value >> 8));
  29. }
  30. void AppendUInt32(std::vector<VGears::uint8>& data, VGears::uint32 value){
  31. data.push_back(static_cast<VGears::uint8>(value & 0xFF));
  32. data.push_back(static_cast<VGears::uint8>((value >> 8) & 0xFF));
  33. data.push_back(static_cast<VGears::uint8>((value >> 16) & 0xFF));
  34. data.push_back(static_cast<VGears::uint8>((value >> 24) & 0xFF));
  35. }
  36. void WriteUInt32(
  37. std::vector<VGears::uint8>& data, size_t offset, VGears::uint32 value
  38. ){
  39. data[offset] = static_cast<VGears::uint8>(value & 0xFF);
  40. data[offset + 1] = static_cast<VGears::uint8>((value >> 8) & 0xFF);
  41. data[offset + 2] = static_cast<VGears::uint8>((value >> 16) & 0xFF);
  42. data[offset + 3] = static_cast<VGears::uint8>((value >> 24) & 0xFF);
  43. }
  44. VGears::uint32 ReadUInt32(
  45. const std::vector<VGears::uint8>& data, size_t offset
  46. ){
  47. return static_cast<VGears::uint32>(data[offset])
  48. | (static_cast<VGears::uint32>(data[offset + 1]) << 8)
  49. | (static_cast<VGears::uint32>(data[offset + 2]) << 16)
  50. | (static_cast<VGears::uint32>(data[offset + 3]) << 24);
  51. }
  52. std::vector<VGears::uint8> CreateMeshData(
  53. bool truncated, bool invalid_vertex
  54. ){
  55. std::vector<VGears::uint8> data;
  56. AppendUInt16(data, 1);
  57. AppendUInt16(data, 3);
  58. if (truncated) return data;
  59. data.push_back(invalid_vertex ? 3 : 0);
  60. data.push_back(1);
  61. data.push_back(2);
  62. data.push_back(0xA5);
  63. data.push_back(10);
  64. data.push_back(20);
  65. data.push_back(30);
  66. data.push_back(40);
  67. data.push_back(50);
  68. data.push_back(60);
  69. AppendUInt16(data, 0xAB55);
  70. for (int vertex_index = 0; vertex_index < 3; ++vertex_index){
  71. AppendUInt16(data, static_cast<VGears::uint16>(-100 + vertex_index));
  72. AppendUInt16(data, static_cast<VGears::uint16>(200 + vertex_index));
  73. AppendUInt16(data, static_cast<VGears::uint16>(-300 + vertex_index));
  74. AppendUInt16(data, 0);
  75. }
  76. for (int normal_index = 0; normal_index < 3; ++normal_index){
  77. AppendUInt16(data, static_cast<VGears::uint16>(normal_index + 1));
  78. AppendUInt16(data, static_cast<VGears::uint16>(normal_index + 2));
  79. AppendUInt16(data, static_cast<VGears::uint16>(normal_index + 3));
  80. AppendUInt16(data, 0);
  81. }
  82. return data;
  83. }
  84. std::vector<VGears::uint8> EncodeLzs(
  85. const std::vector<VGears::uint8>& data
  86. ){
  87. std::vector<VGears::uint8> payload;
  88. size_t data_offset = 0;
  89. while (data_offset < data.size()){
  90. const size_t control_offset = payload.size();
  91. payload.push_back(0);
  92. VGears::uint8 control = 0;
  93. for (unsigned int bit = 0; bit < 8 && data_offset < data.size(); ++bit){
  94. control |= static_cast<VGears::uint8>(1u << bit);
  95. payload.push_back(data[data_offset++]);
  96. }
  97. payload[control_offset] = control;
  98. }
  99. std::vector<VGears::uint8> compressed;
  100. AppendUInt32(compressed, static_cast<VGears::uint32>(payload.size()));
  101. compressed.insert(compressed.end(), payload.begin(), payload.end());
  102. return compressed;
  103. }
  104. std::vector<VGears::uint8> CreateMapFixture(
  105. bool truncated_mesh = false, bool invalid_vertex = false
  106. ){
  107. std::vector<VGears::uint8> data(WORLD_MAP_BLOCK_SIZE, 0);
  108. size_t write_offset = sizeof(VGears::uint32) * 16;
  109. for (size_t mesh_index = 0; mesh_index < 16; ++mesh_index){
  110. const std::vector<VGears::uint8> mesh_data = mesh_index == 0
  111. ? CreateMeshData(truncated_mesh, invalid_vertex)
  112. : std::vector<VGears::uint8>{0, 0, 0, 0};
  113. const std::vector<VGears::uint8> compressed = EncodeLzs(mesh_data);
  114. write_offset = (write_offset + 3) & ~static_cast<size_t>(3);
  115. WriteUInt32(data, mesh_index * sizeof(VGears::uint32),
  116. static_cast<VGears::uint32>(write_offset));
  117. std::copy(compressed.begin(), compressed.end(), data.begin() + write_offset);
  118. write_offset += compressed.size();
  119. }
  120. return data;
  121. }
  122. void ImportFixture(std::vector<VGears::uint8>& data){
  123. VGears::WorldmapFileSerializer serializer;
  124. TestWorldmapFile file;
  125. Ogre::DataStreamPtr stream(OGRE_NEW Ogre::MemoryDataStream(
  126. data.data(), data.size(), false, true
  127. ));
  128. serializer.ImportWorldmapFile(stream, file);
  129. }
  130. }
  131. BOOST_AUTO_TEST_CASE(TestMapFileSerializerReadsPackedTriangleFields){
  132. std::vector<VGears::uint8> data = CreateMapFixture();
  133. VGears::WorldmapFileSerializer serializer;
  134. TestWorldmapFile file;
  135. Ogre::DataStreamPtr stream(OGRE_NEW Ogre::MemoryDataStream(
  136. data.data(), data.size(), false, true
  137. ));
  138. serializer.ImportWorldmapFile(stream, file);
  139. BOOST_REQUIRE_EQUAL(serializer.blocks.size(), 1u);
  140. BOOST_REQUIRE_EQUAL(serializer.blocks[0].meshes.size(), 16u);
  141. const VGears::WorldmapFileSerializer::BlockPart& part
  142. = serializer.blocks[0].meshes[0];
  143. BOOST_CHECK_EQUAL(part.header.triengle_count, 1);
  144. BOOST_CHECK_EQUAL(part.header.vertex_count, 3);
  145. BOOST_REQUIRE_EQUAL(part.triangles.size(), 1u);
  146. const VGears::WorldmapFileSerializer::BlockTriangle& triangle
  147. = part.triangles[0];
  148. BOOST_CHECK_EQUAL(triangle.walkability_info_, 5);
  149. BOOST_CHECK_EQUAL(triangle.unknown, 5);
  150. BOOST_CHECK_EQUAL(triangle.vertex_0_u, 10);
  151. BOOST_CHECK_EQUAL(triangle.vertex_0_v, 20);
  152. BOOST_CHECK_EQUAL(triangle.vertex_1_u, 30);
  153. BOOST_CHECK_EQUAL(triangle.vertex_1_v, 40);
  154. BOOST_CHECK_EQUAL(triangle.vertex_2_u, 50);
  155. BOOST_CHECK_EQUAL(triangle.vertex_2_v, 60);
  156. BOOST_CHECK_EQUAL(triangle.TextureInfo, 0x155);
  157. BOOST_CHECK_EQUAL(triangle.Location, 0x55);
  158. BOOST_REQUIRE_EQUAL(part.vertices.size(), 3u);
  159. BOOST_CHECK_EQUAL(part.vertices[0].x, -100);
  160. BOOST_CHECK_EQUAL(part.vertices[0].y, 200);
  161. BOOST_CHECK_EQUAL(part.vertices[0].z, -300);
  162. BOOST_REQUIRE_EQUAL(part.normals.size(), 3u);
  163. BOOST_CHECK_EQUAL(part.normals[0].x, 1);
  164. BOOST_CHECK_EQUAL(part.normals[0].y, 2);
  165. BOOST_CHECK_EQUAL(part.normals[0].z, 3);
  166. }
  167. BOOST_AUTO_TEST_CASE(TestMapFileSerializerRejectsPartialBlocks){
  168. std::vector<VGears::uint8> data = CreateMapFixture();
  169. data.pop_back();
  170. BOOST_CHECK_THROW(ImportFixture(data), Ogre::Exception);
  171. }
  172. BOOST_AUTO_TEST_CASE(TestMapFileSerializerRejectsInvalidMeshOffsets){
  173. std::vector<VGears::uint8> data = CreateMapFixture();
  174. WriteUInt32(data, 0, static_cast<VGears::uint32>(WORLD_MAP_BLOCK_SIZE - 2));
  175. BOOST_CHECK_THROW(ImportFixture(data), Ogre::Exception);
  176. }
  177. BOOST_AUTO_TEST_CASE(TestMapFileSerializerRejectsMeshesOutsideBlock){
  178. std::vector<VGears::uint8> data = CreateMapFixture();
  179. const size_t mesh_offset = WORLD_MAP_BLOCK_SIZE - sizeof(VGears::uint32);
  180. WriteUInt32(data, 0, static_cast<VGears::uint32>(mesh_offset));
  181. WriteUInt32(data, mesh_offset, 1);
  182. BOOST_CHECK_THROW(ImportFixture(data), Ogre::Exception);
  183. }
  184. BOOST_AUTO_TEST_CASE(TestMapFileSerializerRejectsTruncatedLzsTokens){
  185. std::vector<VGears::uint8> data = CreateMapFixture();
  186. const size_t mesh_offset = ReadUInt32(data, 0);
  187. WriteUInt32(data, mesh_offset, 2);
  188. data[mesh_offset + sizeof(VGears::uint32)] = 0;
  189. data[mesh_offset + sizeof(VGears::uint32) + 1] = 0x10;
  190. BOOST_CHECK_THROW(ImportFixture(data), Ogre::Exception);
  191. }
  192. BOOST_AUTO_TEST_CASE(TestMapFileSerializerRejectsTruncatedMeshes){
  193. std::vector<VGears::uint8> data = CreateMapFixture(true);
  194. BOOST_CHECK_THROW(ImportFixture(data), Ogre::Exception);
  195. }
  196. BOOST_AUTO_TEST_CASE(TestMapFileSerializerRejectsInvalidVertexIndices){
  197. std::vector<VGears::uint8> data = CreateMapFixture(false, true);
  198. BOOST_CHECK_THROW(ImportFixture(data), Ogre::Exception);
  199. }