CDefHandler.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. * CDefHandler.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 "SDL.h"
  12. #include "CDefHandler.h"
  13. #include "../lib/filesystem/Filesystem.h"
  14. #include "../lib/VCMI_Lib.h"
  15. #include "CBitmapHandler.h"
  16. #include "gui/SDL_Extensions.h"
  17. #ifdef unused
  18. static long long pow(long long a, int b)
  19. {
  20. if(!b)
  21. return 1;
  22. long c = a;
  23. while(--b)
  24. a *= c;
  25. return a;
  26. }
  27. #endif
  28. CDefHandler::CDefHandler()
  29. {
  30. notFreeImgs = false;
  31. }
  32. CDefHandler::~CDefHandler()
  33. {
  34. if(notFreeImgs)
  35. return;
  36. for(auto & elem : ourImages)
  37. {
  38. if(elem.bitmap)
  39. {
  40. SDL_FreeSurface(elem.bitmap);
  41. elem.bitmap = nullptr;
  42. }
  43. }
  44. }
  45. CDefEssential::~CDefEssential()
  46. {
  47. for(auto & elem : ourImages)
  48. SDL_FreeSurface(elem.bitmap);
  49. }
  50. void CDefHandler::openFromMemory(ui8 * table, const std::string & name)
  51. {
  52. SDL_Color palette[256];
  53. SDefEntry & de = *reinterpret_cast<SDefEntry *>(table);
  54. ui8 * p;
  55. defName = name;
  56. DEFType = read_le_u32(&de.DEFType);
  57. width = read_le_u32(&de.width);
  58. height = read_le_u32(&de.height);
  59. ui32 totalBlocks = read_le_u32(&de.totalBlocks);
  60. for(ui32 it = 0; it < 256; it++)
  61. {
  62. palette[it].r = de.palette[it].R;
  63. palette[it].g = de.palette[it].G;
  64. palette[it].b = de.palette[it].B;
  65. palette[it].a = SDL_ALPHA_OPAQUE;
  66. }
  67. // The SDefEntryBlock starts just after the SDefEntry
  68. p = reinterpret_cast<ui8 *>(&de);
  69. p += sizeof(de);
  70. int totalEntries = 0;
  71. for(ui32 z = 0; z < totalBlocks; z++)
  72. {
  73. SDefEntryBlock & block = *reinterpret_cast<SDefEntryBlock *>(p);
  74. ui32 totalInBlock;
  75. totalInBlock = read_le_u32(&block.totalInBlock);
  76. for(ui32 j = SEntries.size(); j < totalEntries + totalInBlock; j++)
  77. SEntries.push_back(SEntry());
  78. p = block.data;
  79. for(ui32 j = 0; j < totalInBlock; j++)
  80. {
  81. char Buffer[13];
  82. memcpy(Buffer, p, 12);
  83. Buffer[12] = 0;
  84. SEntries[totalEntries + j].name = Buffer;
  85. p += 13;
  86. }
  87. for(ui32 j = 0; j < totalInBlock; j++)
  88. {
  89. SEntries[totalEntries + j].offset = read_le_u32(p);
  90. p += 4;
  91. }
  92. //totalEntries+=totalInBlock;
  93. for(ui32 hh = 0; hh < totalInBlock; ++hh)
  94. {
  95. SEntries[totalEntries].group = z;
  96. ++totalEntries;
  97. }
  98. }
  99. for(auto & elem : SEntries)
  100. {
  101. elem.name = elem.name.substr(0, elem.name.find('.') + 4);
  102. }
  103. //RWEntries = new ui32[height];
  104. for(ui32 i = 0; i < SEntries.size(); ++i)
  105. {
  106. Cimage nimg;
  107. nimg.bitmap = getSprite(i, table, palette);
  108. nimg.imName = SEntries[i].name;
  109. nimg.groupNumber = SEntries[i].group;
  110. ourImages.push_back(nimg);
  111. }
  112. }
  113. SDL_Surface * CDefHandler::getSprite(int SIndex, const ui8 * FDef, const SDL_Color * palette) const
  114. {
  115. SDL_Surface * ret = nullptr;
  116. ui32 BaseOffset,
  117. SpriteWidth, SpriteHeight, //format of sprite
  118. TotalRowLength, // length of read segment
  119. add, FullHeight, FullWidth,
  120. RowAdd,
  121. defType2;
  122. int LeftMargin, RightMargin, TopMargin, BottomMargin;
  123. ui8 SegmentType;
  124. BaseOffset = SEntries[SIndex].offset;
  125. SSpriteDef sd = *reinterpret_cast<const SSpriteDef *>(FDef + BaseOffset);
  126. defType2 = read_le_u32(&sd.defType2);
  127. FullWidth = read_le_u32(&sd.FullWidth);
  128. FullHeight = read_le_u32(&sd.FullHeight);
  129. SpriteWidth = read_le_u32(&sd.SpriteWidth);
  130. SpriteHeight = read_le_u32(&sd.SpriteHeight);
  131. LeftMargin = read_le_u32(&sd.LeftMargin);
  132. TopMargin = read_le_u32(&sd.TopMargin);
  133. RightMargin = FullWidth - SpriteWidth - LeftMargin;
  134. BottomMargin = FullHeight - SpriteHeight - TopMargin;
  135. //if(LeftMargin + RightMargin < 0)
  136. // SpriteWidth += LeftMargin + RightMargin; //ugly construction... TODO: check how to do it nicer
  137. if(LeftMargin < 0)
  138. SpriteWidth += LeftMargin;
  139. if(RightMargin < 0)
  140. SpriteWidth += RightMargin;
  141. // Note: this looks bogus because we allocate only FullWidth, not FullWidth+add
  142. add = 4 - FullWidth % 4;
  143. if(add == 4)
  144. add = 0;
  145. ret = SDL_CreateRGBSurface(SDL_SWSURFACE, FullWidth, FullHeight, 8, 0, 0, 0, 0);
  146. if(nullptr == ret)
  147. {
  148. logGlobal->errorStream() << __FUNCTION__ << ": Unable to create surface";
  149. logGlobal->errorStream() << FullWidth << "X" << FullHeight;
  150. logGlobal->errorStream() << SDL_GetError();
  151. throw std::runtime_error("Unable to create surface");
  152. }
  153. BaseOffset += sizeof(SSpriteDef);
  154. int BaseOffsetor = BaseOffset;
  155. SDL_Palette * p = SDL_AllocPalette(256);
  156. SDL_SetPaletteColors(p, palette, 0, 256);
  157. SDL_SetSurfacePalette(ret, p);
  158. SDL_FreePalette(p);
  159. int ftcp = 0;
  160. // If there's a margin anywhere, just blank out the whole surface.
  161. if(TopMargin > 0 || BottomMargin > 0 || LeftMargin > 0 || RightMargin > 0)
  162. {
  163. memset(reinterpret_cast<char *>(ret->pixels), 0, FullHeight * FullWidth);
  164. }
  165. // Skip top margin
  166. if(TopMargin > 0)
  167. ftcp += TopMargin * (FullWidth + add);
  168. switch(defType2)
  169. {
  170. case 0:
  171. {
  172. for(ui32 i = 0; i < SpriteHeight; i++)
  173. {
  174. if(LeftMargin > 0)
  175. ftcp += LeftMargin;
  176. memcpy(reinterpret_cast<char *>(ret->pixels) + ftcp, &FDef[BaseOffset], SpriteWidth);
  177. ftcp += SpriteWidth;
  178. BaseOffset += SpriteWidth;
  179. if(RightMargin > 0)
  180. ftcp += RightMargin;
  181. }
  182. }
  183. break;
  184. case 1:
  185. {
  186. const ui32 * RWEntriesLoc = reinterpret_cast<const ui32 *>(FDef + BaseOffset);
  187. BaseOffset += sizeof(int) * SpriteHeight;
  188. for(ui32 i = 0; i < SpriteHeight; i++)
  189. {
  190. BaseOffset = BaseOffsetor + read_le_u32(RWEntriesLoc + i);
  191. if(LeftMargin > 0)
  192. ftcp += LeftMargin;
  193. TotalRowLength = 0;
  194. do
  195. {
  196. ui32 SegmentLength;
  197. SegmentType = FDef[BaseOffset++];
  198. SegmentLength = FDef[BaseOffset++] + 1;
  199. if(SegmentType == 0xFF)
  200. {
  201. memcpy(reinterpret_cast<char *>(ret->pixels) + ftcp, FDef + BaseOffset, SegmentLength);
  202. BaseOffset += SegmentLength;
  203. }
  204. else
  205. {
  206. memset(reinterpret_cast<char *>(ret->pixels) + ftcp, SegmentType, SegmentLength);
  207. }
  208. ftcp += SegmentLength;
  209. TotalRowLength += SegmentLength;
  210. }
  211. while(TotalRowLength < SpriteWidth);
  212. RowAdd = SpriteWidth - TotalRowLength;
  213. if(RightMargin > 0)
  214. ftcp += RightMargin;
  215. if(add > 0)
  216. ftcp += add + RowAdd;
  217. }
  218. }
  219. break;
  220. case 2:
  221. {
  222. BaseOffset = BaseOffsetor + read_le_u16(FDef + BaseOffsetor);
  223. for(ui32 i = 0; i < SpriteHeight; i++)
  224. {
  225. //BaseOffset = BaseOffsetor+RWEntries[i];
  226. if(LeftMargin > 0)
  227. ftcp += LeftMargin;
  228. TotalRowLength = 0;
  229. do
  230. {
  231. SegmentType = FDef[BaseOffset++];
  232. ui8 code = SegmentType / 32;
  233. ui8 value = (SegmentType & 31) + 1;
  234. if(code == 7)
  235. {
  236. memcpy(reinterpret_cast<char *>(ret->pixels) + ftcp, &FDef[BaseOffset], value);
  237. ftcp += value;
  238. BaseOffset += value;
  239. }
  240. else
  241. {
  242. memset(reinterpret_cast<char *>(ret->pixels) + ftcp, code, value);
  243. ftcp += value;
  244. }
  245. TotalRowLength += value;
  246. }
  247. while(TotalRowLength < SpriteWidth);
  248. if(RightMargin > 0)
  249. ftcp += RightMargin;
  250. RowAdd = SpriteWidth - TotalRowLength;
  251. if(add > 0)
  252. ftcp += add + RowAdd;
  253. }
  254. }
  255. break;
  256. case 3:
  257. {
  258. for(ui32 i = 0; i < SpriteHeight; i++)
  259. {
  260. BaseOffset = BaseOffsetor + read_le_u16(FDef + BaseOffsetor + i * 2 * (SpriteWidth / 32));
  261. if(LeftMargin > 0)
  262. ftcp += LeftMargin;
  263. TotalRowLength = 0;
  264. do
  265. {
  266. SegmentType = FDef[BaseOffset++];
  267. ui8 code = SegmentType / 32;
  268. ui8 value = (SegmentType & 31) + 1;
  269. int len = std::min<ui32>(value, SpriteWidth - TotalRowLength) - std::max(0, -LeftMargin);
  270. vstd::amax(len, 0);
  271. if(code == 7)
  272. {
  273. memcpy((ui8 *)ret->pixels + ftcp, FDef + BaseOffset, len);
  274. ftcp += len;
  275. BaseOffset += len;
  276. }
  277. else
  278. {
  279. memset((ui8 *)ret->pixels + ftcp, code, len);
  280. ftcp += len;
  281. }
  282. TotalRowLength += (LeftMargin >= 0 ? value : value + LeftMargin);
  283. }
  284. while(TotalRowLength < SpriteWidth);
  285. if(RightMargin > 0)
  286. ftcp += RightMargin;
  287. RowAdd = SpriteWidth - TotalRowLength;
  288. if(add > 0)
  289. ftcp += add + RowAdd;
  290. }
  291. }
  292. break;
  293. default:
  294. throw std::runtime_error("Unknown sprite format.");
  295. break;
  296. }
  297. SDL_Color ttcol = ret->format->palette->colors[0];
  298. Uint32 keycol = SDL_MapRGBA(ret->format, ttcol.r, ttcol.b, ttcol.g, ttcol.a);
  299. SDL_SetColorKey(ret, SDL_TRUE, keycol);
  300. return ret;
  301. }
  302. CDefEssential * CDefHandler::essentialize()
  303. {
  304. auto ret = new CDefEssential();
  305. ret->ourImages = ourImages;
  306. notFreeImgs = true;
  307. return ret;
  308. }
  309. CDefHandler * CDefHandler::giveDef(const std::string & defName)
  310. {
  311. ResourceID resID(std::string("SPRITES/") + defName, EResType::ANIMATION);
  312. auto data = CResourceHandler::get()->load(resID)->readAll().first;
  313. if(!data)
  314. throw std::runtime_error("bad def name!");
  315. auto nh = new CDefHandler();
  316. nh->openFromMemory(data.get(), defName);
  317. return nh;
  318. }
  319. CDefEssential * CDefHandler::giveDefEss(const std::string & defName)
  320. {
  321. CDefEssential * ret;
  322. CDefHandler * temp = giveDef(defName);
  323. ret = temp->essentialize();
  324. delete temp;
  325. return ret;
  326. }