WorldmapFileSerializer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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 "WorldmapFileSerializer.h"
  16. #include <OgreException.h>
  17. #include <string>
  18. #include "common/LzsFile.h"
  19. namespace VGears {
  20. namespace {
  21. const size_t WORLD_MAP_BLOCK_SIZE = 0xB800;
  22. const size_t WORLD_MAP_BLOCK_HEADER_SIZE = sizeof(uint32) * 16;
  23. const size_t WORLD_MAP_TRIANGLE_SIZE = 12;
  24. const size_t WORLD_MAP_VERTEX_SIZE = 8;
  25. uint16 ReadLittleEndian16(const std::vector<uint8>& data, size_t offset){
  26. return static_cast<uint16>(data[offset])
  27. | (static_cast<uint16>(data[offset + 1]) << 8);
  28. }
  29. uint32 ReadLittleEndian32(const std::vector<uint8>& data, size_t offset){
  30. return static_cast<uint32>(data[offset])
  31. | (static_cast<uint32>(data[offset + 1]) << 8)
  32. | (static_cast<uint32>(data[offset + 2]) << 16)
  33. | (static_cast<uint32>(data[offset + 3]) << 24);
  34. }
  35. sint16 ReadLittleEndianSigned16(
  36. const std::vector<uint8>& data, size_t offset
  37. ){
  38. const uint16 value = ReadLittleEndian16(data, offset);
  39. const sint32 signed_value = value <= 0x7FFF
  40. ? static_cast<sint32>(value)
  41. : static_cast<sint32>(value) - 0x10000;
  42. return static_cast<sint16>(signed_value);
  43. }
  44. void ThrowInvalidWorldmap(const std::string& reason){
  45. OGRE_EXCEPT(
  46. Ogre::Exception::ERR_INVALIDPARAMS,
  47. "Invalid world map file: " + reason,
  48. "WorldmapFileSerializer::ImportWorldmapFile"
  49. );
  50. }
  51. void ValidateLzsData(
  52. const std::vector<uint8>& data, size_t max_output_size
  53. ){
  54. if (data.size() <= 4
  55. || ReadLittleEndian32(data, 0) != data.size() - 4){
  56. ThrowInvalidWorldmap("invalid LZS payload size");
  57. }
  58. size_t input_offset = 4;
  59. size_t output_size = 0;
  60. while (input_offset < data.size()){
  61. const uint8 control = data[input_offset++];
  62. bool has_token = false;
  63. for (unsigned int bit = 0;
  64. bit < 8 && input_offset < data.size(); ++bit){
  65. has_token = true;
  66. size_t token_output_size = 1;
  67. if ((control & (1u << bit)) == 0){
  68. if (data.size() - input_offset < 2){
  69. ThrowInvalidWorldmap("truncated LZS back-reference");
  70. }
  71. token_output_size
  72. = static_cast<size_t>(data[input_offset + 1] & 0x0F) + 3;
  73. input_offset += 2;
  74. }
  75. else ++input_offset;
  76. if (token_output_size > max_output_size - output_size){
  77. ThrowInvalidWorldmap("LZS output exceeds a MAP block");
  78. }
  79. output_size += token_output_size;
  80. }
  81. if (!has_token){
  82. ThrowInvalidWorldmap("truncated LZS control byte");
  83. }
  84. }
  85. }
  86. }
  87. WorldmapFileSerializer::WorldmapFileSerializer(){}
  88. WorldmapFileSerializer::~WorldmapFileSerializer(){}
  89. void WorldmapFileSerializer::ImportWorldmapFile(
  90. Ogre::DataStreamPtr& stream, WorldmapFile& dest
  91. ){
  92. static_cast<void>(dest);
  93. if (stream.isNull()){
  94. ThrowInvalidWorldmap("input stream is null");
  95. }
  96. const size_t file_size = stream->size();
  97. if (file_size == 0 || file_size % WORLD_MAP_BLOCK_SIZE != 0){
  98. ThrowInvalidWorldmap("file size is not a whole number of MAP blocks");
  99. }
  100. std::vector<uint8> file_data(file_size);
  101. stream->seek(0);
  102. if (stream->read(file_data.data(), file_data.size()) != file_data.size()){
  103. ThrowInvalidWorldmap("could not read the complete file");
  104. }
  105. std::vector<Block> parsed_blocks;
  106. const size_t block_count = file_size / WORLD_MAP_BLOCK_SIZE;
  107. parsed_blocks.reserve(block_count);
  108. for (size_t block_index = 0; block_index < block_count; ++block_index){
  109. Block block;
  110. const size_t block_start = block_index * WORLD_MAP_BLOCK_SIZE;
  111. block.meshes.reserve(16);
  112. for (size_t mesh_index = 0; mesh_index < 16; ++mesh_index){
  113. const uint32 mesh_offset = ReadLittleEndian32(
  114. file_data, block_start + mesh_index * sizeof(uint32)
  115. );
  116. if (mesh_offset < WORLD_MAP_BLOCK_HEADER_SIZE
  117. || mesh_offset % 4 != 0
  118. || mesh_offset > WORLD_MAP_BLOCK_SIZE - sizeof(uint32)){
  119. ThrowInvalidWorldmap("mesh offset is invalid");
  120. }
  121. const size_t mesh_start = block_start + mesh_offset;
  122. const uint32 compressed_data_size
  123. = ReadLittleEndian32(file_data, mesh_start);
  124. const size_t max_compressed_size
  125. = WORLD_MAP_BLOCK_SIZE - mesh_offset - sizeof(uint32);
  126. if (compressed_data_size > max_compressed_size){
  127. ThrowInvalidWorldmap("compressed mesh extends past its MAP block");
  128. }
  129. const size_t compressed_size
  130. = static_cast<size_t>(compressed_data_size) + sizeof(uint32);
  131. std::vector<uint8> compressed_data(
  132. file_data.begin() + mesh_start,
  133. file_data.begin() + mesh_start + compressed_size
  134. );
  135. ValidateLzsData(compressed_data, WORLD_MAP_BLOCK_SIZE);
  136. const std::vector<uint8> decompressed
  137. = LzsBuffer::Decompress(compressed_data);
  138. if (decompressed.size() < 4){
  139. ThrowInvalidWorldmap("decompressed mesh header is truncated");
  140. }
  141. BlockPart block_part;
  142. block_part.header.triengle_count
  143. = ReadLittleEndian16(decompressed, 0);
  144. block_part.header.vertex_count
  145. = ReadLittleEndian16(decompressed, 2);
  146. const size_t expected_size = 4
  147. + static_cast<size_t>(block_part.header.triengle_count)
  148. * WORLD_MAP_TRIANGLE_SIZE
  149. + static_cast<size_t>(block_part.header.vertex_count)
  150. * WORLD_MAP_VERTEX_SIZE * 2;
  151. if (expected_size > decompressed.size()){
  152. ThrowInvalidWorldmap("decompressed mesh data is truncated");
  153. }
  154. size_t data_offset = 4;
  155. block_part.triangles.resize(block_part.header.triengle_count);
  156. for (size_t triangle_index = 0;
  157. triangle_index < block_part.triangles.size(); ++triangle_index){
  158. BlockTriangle& triangle = block_part.triangles[triangle_index];
  159. triangle.vertex_0_index = decompressed[data_offset++];
  160. triangle.vertex_1_index = decompressed[data_offset++];
  161. triangle.vertex_2_index = decompressed[data_offset++];
  162. const uint8 walkmap_and_function = decompressed[data_offset++];
  163. triangle.walkability_info_ = walkmap_and_function & 0x1F;
  164. triangle.function_id = (walkmap_and_function >> 5) & 0x07;
  165. triangle.vertex_0_u = decompressed[data_offset++];
  166. triangle.vertex_0_v = decompressed[data_offset++];
  167. triangle.vertex_1_u = decompressed[data_offset++];
  168. triangle.vertex_1_v = decompressed[data_offset++];
  169. triangle.vertex_2_u = decompressed[data_offset++];
  170. triangle.vertex_2_v = decompressed[data_offset++];
  171. const uint16 texture_and_location
  172. = ReadLittleEndian16(decompressed, data_offset);
  173. data_offset += sizeof(uint16);
  174. triangle.TextureInfo = texture_and_location & 0x1FF;
  175. triangle.Location = (texture_and_location >> 9) & 0x7F;
  176. if (triangle.vertex_0_index >= block_part.header.vertex_count
  177. || triangle.vertex_1_index >= block_part.header.vertex_count
  178. || triangle.vertex_2_index >= block_part.header.vertex_count){
  179. ThrowInvalidWorldmap("triangle references a missing vertex");
  180. }
  181. }
  182. block_part.vertices.resize(block_part.header.vertex_count);
  183. for (size_t vertex_index = 0;
  184. vertex_index < block_part.vertices.size(); ++vertex_index){
  185. Vertex& vertex = block_part.vertices[vertex_index];
  186. vertex.x = ReadLittleEndianSigned16(decompressed, data_offset);
  187. data_offset += sizeof(uint16);
  188. vertex.y = ReadLittleEndianSigned16(decompressed, data_offset);
  189. data_offset += sizeof(uint16);
  190. vertex.z = ReadLittleEndianSigned16(decompressed, data_offset);
  191. data_offset += sizeof(uint16);
  192. vertex.unused = ReadLittleEndian16(decompressed, data_offset);
  193. data_offset += sizeof(uint16);
  194. }
  195. block_part.normals.resize(block_part.header.vertex_count);
  196. for (size_t normal_index = 0;
  197. normal_index < block_part.normals.size(); ++normal_index){
  198. Normal& normal = block_part.normals[normal_index];
  199. normal.x = ReadLittleEndianSigned16(decompressed, data_offset);
  200. data_offset += sizeof(uint16);
  201. normal.y = ReadLittleEndianSigned16(decompressed, data_offset);
  202. data_offset += sizeof(uint16);
  203. normal.z = ReadLittleEndianSigned16(decompressed, data_offset);
  204. data_offset += sizeof(uint16);
  205. normal.unused = ReadLittleEndian16(decompressed, data_offset);
  206. data_offset += sizeof(uint16);
  207. }
  208. block.meshes.push_back(block_part);
  209. }
  210. parsed_blocks.push_back(block);
  211. }
  212. blocks.swap(parsed_blocks);
  213. }
  214. }