CDefFile.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. * CDefFile.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "CAnimation.h"
  12. #include "SDL_Extensions.h"
  13. #include "ColorFilter.h"
  14. #include "../CBitmapHandler.h"
  15. #include "../Graphics.h"
  16. #include "../../lib/filesystem/Filesystem.h"
  17. #include "../../lib/filesystem/ISimpleResourceLoader.h"
  18. #include "../../lib/JsonNode.h"
  19. #include "../../lib/CRandomGenerator.h"
  20. #include "../../lib/vcmi_endian.h"
  21. #include <SDL_surface.h>
  22. // Extremely simple file cache. TODO: smarter, more general solution
  23. class CFileCache
  24. {
  25. static const int cacheSize = 50; //Max number of cached files
  26. struct FileData
  27. {
  28. ResourceID name;
  29. size_t size;
  30. std::unique_ptr<ui8[]> data;
  31. std::unique_ptr<ui8[]> getCopy()
  32. {
  33. auto ret = std::unique_ptr<ui8[]>(new ui8[size]);
  34. std::copy(data.get(), data.get() + size, ret.get());
  35. return ret;
  36. }
  37. FileData(ResourceID name_, size_t size_, std::unique_ptr<ui8[]> data_):
  38. name{std::move(name_)},
  39. size{size_},
  40. data{std::move(data_)}
  41. {}
  42. };
  43. std::deque<FileData> cache;
  44. public:
  45. std::unique_ptr<ui8[]> getCachedFile(ResourceID rid)
  46. {
  47. for(auto & file : cache)
  48. {
  49. if (file.name == rid)
  50. return file.getCopy();
  51. }
  52. // Still here? Cache miss
  53. if (cache.size() > cacheSize)
  54. cache.pop_front();
  55. auto data = CResourceHandler::get()->load(rid)->readAll();
  56. cache.emplace_back(std::move(rid), data.second, std::move(data.first));
  57. return cache.back().getCopy();
  58. }
  59. };
  60. enum class DefType : uint32_t
  61. {
  62. SPELL = 0x40,
  63. SPRITE = 0x41,
  64. CREATURE = 0x42,
  65. MAP = 0x43,
  66. MAP_HERO = 0x44,
  67. TERRAIN = 0x45,
  68. CURSOR = 0x46,
  69. INTERFACE = 0x47,
  70. SPRITE_FRAME = 0x48,
  71. BATTLE_HERO = 0x49
  72. };
  73. static CFileCache animationCache;
  74. /*************************************************************************
  75. * DefFile, class used for def loading *
  76. *************************************************************************/
  77. bool operator== (const SDL_Color & lhs, const SDL_Color & rhs)
  78. {
  79. return (lhs.a == rhs.a) && (lhs.b == rhs.b) &&(lhs.g == rhs.g) &&(lhs.r == rhs.r);
  80. }
  81. CDefFile::CDefFile(std::string Name):
  82. data(nullptr),
  83. palette(nullptr)
  84. {
  85. //First 8 colors in def palette used for transparency
  86. static SDL_Color H3Palette[8] =
  87. {
  88. { 0, 0, 0, 0},// transparency ( used in most images )
  89. { 0, 0, 0, 64},// shadow border ( used in battle, adventure map def's )
  90. { 0, 0, 0, 64},// shadow border ( used in fog-of-war def's )
  91. { 0, 0, 0, 128},// shadow body ( used in fog-of-war def's )
  92. { 0, 0, 0, 128},// shadow body ( used in battle, adventure map def's )
  93. { 0, 0, 0, 0},// selection ( used in battle def's )
  94. { 0, 0, 0, 128},// shadow body below selection ( used in battle def's )
  95. { 0, 0, 0, 64} // shadow border below selection ( used in battle def's )
  96. };
  97. data = animationCache.getCachedFile(ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION));
  98. palette = std::unique_ptr<SDL_Color[]>(new SDL_Color[256]);
  99. int it = 0;
  100. ui32 type = read_le_u32(data.get() + it);
  101. it+=4;
  102. //int width = read_le_u32(data + it); it+=4;//not used
  103. //int height = read_le_u32(data + it); it+=4;
  104. it+=8;
  105. ui32 totalBlocks = read_le_u32(data.get() + it);
  106. it+=4;
  107. for (ui32 i= 0; i<256; i++)
  108. {
  109. palette[i].r = data[it++];
  110. palette[i].g = data[it++];
  111. palette[i].b = data[it++];
  112. palette[i].a = SDL_ALPHA_OPAQUE;
  113. }
  114. switch(static_cast<DefType>(type))
  115. {
  116. case DefType::SPELL:
  117. palette[0] = H3Palette[0];
  118. break;
  119. case DefType::SPRITE:
  120. case DefType::SPRITE_FRAME:
  121. for(ui32 i= 0; i<8; i++)
  122. palette[i] = H3Palette[i];
  123. break;
  124. case DefType::CREATURE:
  125. palette[0] = H3Palette[0];
  126. palette[1] = H3Palette[1];
  127. palette[4] = H3Palette[4];
  128. palette[5] = H3Palette[5];
  129. palette[6] = H3Palette[6];
  130. palette[7] = H3Palette[7];
  131. break;
  132. case DefType::MAP:
  133. case DefType::MAP_HERO:
  134. palette[0] = H3Palette[0];
  135. palette[1] = H3Palette[1];
  136. palette[4] = H3Palette[4];
  137. //5 = owner flag, handled separately
  138. break;
  139. case DefType::TERRAIN:
  140. palette[0] = H3Palette[0];
  141. palette[1] = H3Palette[1];
  142. palette[2] = H3Palette[2];
  143. palette[3] = H3Palette[3];
  144. palette[4] = H3Palette[4];
  145. break;
  146. case DefType::CURSOR:
  147. palette[0] = H3Palette[0];
  148. break;
  149. case DefType::INTERFACE:
  150. palette[0] = H3Palette[0];
  151. palette[1] = H3Palette[1];
  152. palette[4] = H3Palette[4];
  153. //player colors handled separately
  154. //TODO: disallow colorizing other def types
  155. break;
  156. case DefType::BATTLE_HERO:
  157. palette[0] = H3Palette[0];
  158. palette[1] = H3Palette[1];
  159. palette[4] = H3Palette[4];
  160. break;
  161. default:
  162. logAnim->error("Unknown def type %d in %s", type, Name);
  163. break;
  164. }
  165. for (ui32 i=0; i<totalBlocks; i++)
  166. {
  167. size_t blockID = read_le_u32(data.get() + it);
  168. it+=4;
  169. size_t totalEntries = read_le_u32(data.get() + it);
  170. it+=12;
  171. //8 unknown bytes - skipping
  172. //13 bytes for name of every frame in this block - not used, skipping
  173. it+= 13 * (int)totalEntries;
  174. for (ui32 j=0; j<totalEntries; j++)
  175. {
  176. size_t currOffset = read_le_u32(data.get() + it);
  177. offset[blockID].push_back(currOffset);
  178. it += 4;
  179. }
  180. }
  181. }
  182. template<class ImageLoader>
  183. void CDefFile::loadFrame(size_t frame, size_t group, ImageLoader &loader) const
  184. {
  185. std::map<size_t, std::vector <size_t> >::const_iterator it;
  186. it = offset.find(group);
  187. assert (it != offset.end());
  188. const ui8 * FDef = data.get()+it->second[frame];
  189. const SSpriteDef sd = * reinterpret_cast<const SSpriteDef *>(FDef);
  190. SSpriteDef sprite;
  191. sprite.format = read_le_u32(&sd.format);
  192. sprite.fullWidth = read_le_u32(&sd.fullWidth);
  193. sprite.fullHeight = read_le_u32(&sd.fullHeight);
  194. sprite.width = read_le_u32(&sd.width);
  195. sprite.height = read_le_u32(&sd.height);
  196. sprite.leftMargin = read_le_u32(&sd.leftMargin);
  197. sprite.topMargin = read_le_u32(&sd.topMargin);
  198. ui32 currentOffset = sizeof(SSpriteDef);
  199. //special case for some "old" format defs (SGTWMTA.DEF and SGTWMTB.DEF)
  200. if(sprite.format == 1 && sprite.width > sprite.fullWidth && sprite.height > sprite.fullHeight)
  201. {
  202. sprite.leftMargin = 0;
  203. sprite.topMargin = 0;
  204. sprite.width = sprite.fullWidth;
  205. sprite.height = sprite.fullHeight;
  206. currentOffset -= 16;
  207. }
  208. const ui32 BaseOffset = currentOffset;
  209. loader.init(Point(sprite.width, sprite.height),
  210. Point(sprite.leftMargin, sprite.topMargin),
  211. Point(sprite.fullWidth, sprite.fullHeight), palette.get());
  212. switch(sprite.format)
  213. {
  214. case 0:
  215. {
  216. //pixel data is not compressed, copy data to surface
  217. for(ui32 i=0; i<sprite.height; i++)
  218. {
  219. loader.Load(sprite.width, FDef + currentOffset);
  220. currentOffset += sprite.width;
  221. loader.EndLine();
  222. }
  223. break;
  224. }
  225. case 1:
  226. {
  227. //for each line we have offset of pixel data
  228. const ui32 * RWEntriesLoc = reinterpret_cast<const ui32 *>(FDef+currentOffset);
  229. currentOffset += sizeof(ui32) * sprite.height;
  230. for(ui32 i=0; i<sprite.height; i++)
  231. {
  232. //get position of the line
  233. currentOffset=BaseOffset + read_le_u32(RWEntriesLoc + i);
  234. ui32 TotalRowLength = 0;
  235. while(TotalRowLength<sprite.width)
  236. {
  237. ui8 segmentType = FDef[currentOffset++];
  238. ui32 length = FDef[currentOffset++] + 1;
  239. if(segmentType==0xFF)//Raw data
  240. {
  241. loader.Load(length, FDef + currentOffset);
  242. currentOffset+=length;
  243. }
  244. else// RLE
  245. {
  246. loader.Load(length, segmentType);
  247. }
  248. TotalRowLength += length;
  249. }
  250. loader.EndLine();
  251. }
  252. break;
  253. }
  254. case 2:
  255. {
  256. currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset);
  257. for(ui32 i=0; i<sprite.height; i++)
  258. {
  259. ui32 TotalRowLength=0;
  260. while(TotalRowLength<sprite.width)
  261. {
  262. ui8 segment=FDef[currentOffset++];
  263. ui8 code = segment / 32;
  264. ui8 length = (segment & 31) + 1;
  265. if(code==7)//Raw data
  266. {
  267. loader.Load(length, FDef + currentOffset);
  268. currentOffset += length;
  269. }
  270. else//RLE
  271. {
  272. loader.Load(length, code);
  273. }
  274. TotalRowLength+=length;
  275. }
  276. loader.EndLine();
  277. }
  278. break;
  279. }
  280. case 3:
  281. {
  282. for(ui32 i=0; i<sprite.height; i++)
  283. {
  284. currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset+i*2*(sprite.width/32));
  285. ui32 TotalRowLength=0;
  286. while(TotalRowLength<sprite.width)
  287. {
  288. ui8 segment = FDef[currentOffset++];
  289. ui8 code = segment / 32;
  290. ui8 length = (segment & 31) + 1;
  291. if(code==7)//Raw data
  292. {
  293. loader.Load(length, FDef + currentOffset);
  294. currentOffset += length;
  295. }
  296. else//RLE
  297. {
  298. loader.Load(length, code);
  299. }
  300. TotalRowLength += length;
  301. }
  302. loader.EndLine();
  303. }
  304. break;
  305. }
  306. default:
  307. logGlobal->error("Error: unsupported format of def file: %d", sprite.format);
  308. break;
  309. }
  310. }
  311. CDefFile::~CDefFile() = default;
  312. const std::map<size_t, size_t > CDefFile::getEntries() const
  313. {
  314. std::map<size_t, size_t > ret;
  315. for (auto & elem : offset)
  316. ret[elem.first] = elem.second.size();
  317. return ret;
  318. }