CAnimation.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. #include <iostream>
  2. #include <sstream>
  3. #include <boost/foreach.hpp>
  4. #include "SDL.h"
  5. #include "SDL_image.h"
  6. #include "../client/CBitmapHandler.h"
  7. #include "CAnimation.h"
  8. #include "CLodHandler.h"
  9. /*
  10. * CAnimation.cpp, part of VCMI engine
  11. *
  12. * Authors: listed in file AUTHORS in main folder
  13. *
  14. * License: GNU General Public License v2.0 or later
  15. * Full text of license available in license.txt file, in main folder
  16. *
  17. */
  18. extern DLL_EXPORT CLodHandler *spriteh;
  19. /*************************************************************************
  20. * DefFile, class used for def loading *
  21. *************************************************************************/
  22. bool CDefFile::haveFrame(size_t frame, size_t group) const
  23. {
  24. if (offset.size() > group)
  25. if (offset[group].size() > frame)
  26. return true;
  27. return false;
  28. }
  29. SDL_Surface * CDefFile::loadFrame(size_t frame, size_t group) const
  30. {
  31. if (haveFrame(frame, group))
  32. return loadFrame(( unsigned char * )data+offset[group][frame], colors);
  33. return NULL;
  34. }
  35. SDL_Surface * CDefFile::loadFrame (const unsigned char * FDef, const BMPPalette * palette)
  36. {
  37. SDL_Surface * ret=NULL;
  38. unsigned int BaseOffset,
  39. SpriteWidth, SpriteHeight, //format of sprite
  40. TotalRowLength, // length of read segment
  41. add, FullHeight,FullWidth,
  42. RowAdd,
  43. prSize,
  44. defType2;
  45. int LeftMargin, RightMargin, TopMargin, BottomMargin;
  46. unsigned char SegmentType;
  47. BaseOffset = 0;
  48. SSpriteDef sd = * reinterpret_cast<const SSpriteDef *>(FDef + BaseOffset);
  49. prSize = SDL_SwapLE32(sd.prSize);
  50. defType2 = SDL_SwapLE32(sd.defType2);
  51. FullWidth = SDL_SwapLE32(sd.FullWidth);
  52. FullHeight = SDL_SwapLE32(sd.FullHeight);
  53. SpriteWidth = SDL_SwapLE32(sd.SpriteWidth);
  54. SpriteHeight = SDL_SwapLE32(sd.SpriteHeight);
  55. LeftMargin = SDL_SwapLE32(sd.LeftMargin);
  56. TopMargin = SDL_SwapLE32(sd.TopMargin);
  57. RightMargin = FullWidth - SpriteWidth - LeftMargin;
  58. BottomMargin = FullHeight - SpriteHeight - TopMargin;
  59. //if(LeftMargin + RightMargin < 0)
  60. // SpriteWidth += LeftMargin + RightMargin; //ugly construction... TODO: check how to do it nicer
  61. if (LeftMargin<0)
  62. SpriteWidth+=LeftMargin;
  63. if (RightMargin<0)
  64. SpriteWidth+=RightMargin;
  65. // Note: this looks bogus because we allocate only FullWidth, not FullWidth+add
  66. add = 4 - FullWidth%4;
  67. if (add==4)
  68. add=0;
  69. ret = SDL_CreateRGBSurface(SDL_SWSURFACE, FullWidth, FullHeight, 8, 0, 0, 0, 0);
  70. //int tempee2 = readNormalNr(0,4,((unsigned char *)tempee.c_str()));
  71. BaseOffset += sizeof(SSpriteDef);
  72. int BaseOffsetor = BaseOffset;
  73. for (int i=0; i<256; ++i)
  74. {
  75. SDL_Color pr;
  76. pr.r = palette[i].R;
  77. pr.g = palette[i].G;
  78. pr.b = palette[i].B;
  79. pr.unused = palette[i].F;
  80. (*(ret->format->palette->colors+i))=pr;
  81. }
  82. int ftcp=0;
  83. // If there's a margin anywhere, just blank out the whole surface.
  84. if (TopMargin > 0 || BottomMargin > 0 || LeftMargin > 0 || RightMargin > 0)
  85. {
  86. memset( reinterpret_cast<char*>(ret->pixels), 0, FullHeight*FullWidth);
  87. }
  88. // Skip top margin
  89. if (TopMargin > 0)
  90. ftcp += TopMargin*(FullWidth+add);
  91. switch (defType2)
  92. {
  93. case 0:
  94. {
  95. for (unsigned int i=0; i<SpriteHeight; i++)
  96. {
  97. if (LeftMargin>0)
  98. ftcp += LeftMargin;
  99. memcpy(reinterpret_cast<char*>(ret->pixels)+ftcp, &FDef[BaseOffset], SpriteWidth);
  100. ftcp += SpriteWidth;
  101. BaseOffset += SpriteWidth;
  102. if (RightMargin>0)
  103. ftcp += RightMargin;
  104. }
  105. }
  106. break;
  107. case 1:
  108. {
  109. const unsigned int * RWEntriesLoc = reinterpret_cast<const unsigned int *>(FDef+BaseOffset);
  110. BaseOffset += sizeof(int) * SpriteHeight;
  111. for (unsigned int i=0; i<SpriteHeight; i++)
  112. {
  113. BaseOffset=BaseOffsetor + SDL_SwapLE32(read_unaligned_u32(RWEntriesLoc + i));
  114. if (LeftMargin>0)
  115. ftcp += LeftMargin;
  116. TotalRowLength=0;
  117. do
  118. {
  119. unsigned int SegmentLength;
  120. SegmentType=FDef[BaseOffset++];
  121. SegmentLength=FDef[BaseOffset++] + 1;
  122. if (SegmentType==0xFF)
  123. {
  124. memcpy(reinterpret_cast<char*>(ret->pixels)+ftcp, FDef + BaseOffset, SegmentLength);
  125. BaseOffset+=SegmentLength;
  126. }
  127. else
  128. {
  129. memset(reinterpret_cast<char*>(ret->pixels)+ftcp, SegmentType, SegmentLength);
  130. }
  131. ftcp += SegmentLength;
  132. TotalRowLength += SegmentLength;
  133. }
  134. while (TotalRowLength<SpriteWidth);
  135. RowAdd=SpriteWidth-TotalRowLength;
  136. if (RightMargin>0)
  137. ftcp += RightMargin;
  138. if (add>0)
  139. ftcp += add+RowAdd;
  140. }
  141. }
  142. break;
  143. case 2:
  144. {
  145. BaseOffset = BaseOffsetor + SDL_SwapLE16(read_unaligned_u16(FDef + BaseOffsetor));
  146. for (unsigned int i=0; i<SpriteHeight; i++)
  147. {
  148. //BaseOffset = BaseOffsetor+RWEntries[i];
  149. if (LeftMargin>0)
  150. ftcp += LeftMargin;
  151. TotalRowLength=0;
  152. do
  153. {
  154. SegmentType=FDef[BaseOffset++];
  155. unsigned char code = SegmentType / 32;
  156. unsigned char value = (SegmentType & 31) + 1;
  157. if (code==7)
  158. {
  159. memcpy(reinterpret_cast<char*>(ret->pixels)+ftcp, &FDef[BaseOffset], value);
  160. ftcp += value;
  161. BaseOffset += value;
  162. }
  163. else
  164. {
  165. memset(reinterpret_cast<char*>(ret->pixels)+ftcp, code, value);
  166. ftcp += value;
  167. }
  168. TotalRowLength+=value;
  169. }
  170. while (TotalRowLength<SpriteWidth);
  171. if (RightMargin>0)
  172. ftcp += RightMargin;
  173. RowAdd=SpriteWidth-TotalRowLength;
  174. if (add>0)
  175. ftcp += add+RowAdd;
  176. }
  177. }
  178. break;
  179. case 3:
  180. {
  181. for (unsigned int i=0; i<SpriteHeight; i++)
  182. {
  183. BaseOffset = BaseOffsetor + SDL_SwapLE16(read_unaligned_u16(FDef + BaseOffsetor+i*2*(SpriteWidth/32)));
  184. if (LeftMargin>0)
  185. ftcp += LeftMargin;
  186. TotalRowLength=0;
  187. do
  188. {
  189. SegmentType=FDef[BaseOffset++];
  190. unsigned char code = SegmentType / 32;
  191. unsigned char value = (SegmentType & 31) + 1;
  192. int len = std::min<unsigned int>(value, SpriteWidth - TotalRowLength) - std::max(0, -LeftMargin);
  193. amax(len, 0);
  194. if (code==7)
  195. {
  196. memcpy((ui8*)ret->pixels + ftcp, FDef + BaseOffset, len);
  197. ftcp += len;
  198. BaseOffset += len;
  199. }
  200. else
  201. {
  202. memset((ui8*)ret->pixels + ftcp, code, len);
  203. ftcp += len;
  204. }
  205. TotalRowLength+=( LeftMargin>=0 ? value : value+LeftMargin );
  206. }
  207. while (TotalRowLength<SpriteWidth);
  208. if (RightMargin>0)
  209. ftcp += RightMargin;
  210. RowAdd=SpriteWidth-TotalRowLength;
  211. if (add>0)
  212. ftcp += add+RowAdd;
  213. }
  214. }
  215. break;
  216. default:
  217. throw std::string("Unknown sprite format.");
  218. break;
  219. }
  220. SDL_Color ttcol = ret->format->palette->colors[0];
  221. Uint32 keycol = SDL_MapRGBA(ret->format, ttcol.r, ttcol.b, ttcol.g, ttcol.unused);
  222. SDL_SetColorKey(ret, SDL_SRCCOLORKEY, keycol);
  223. return ret;
  224. };
  225. BMPPalette * CDefFile::getPalette()
  226. {
  227. BMPPalette * ret = new BMPPalette[256];
  228. memcpy(ret, colors, sizeof(BMPPalette)*256);
  229. return ret;
  230. }
  231. CDefFile::CDefFile(std::string Name):data(NULL),colors(NULL)
  232. {
  233. data = spriteh->giveFile(Name, FILE_ANIMATION, &datasize);
  234. if (!data)
  235. {
  236. tlog0<<"Error: file "<< Name <<" not found\n";
  237. return;
  238. }
  239. colors = new BMPPalette[256];
  240. int it = 0;
  241. //int type = readNormalNr(data, it); it+=4;
  242. //int width = readNormalNr(data, it); it+=4;//not used
  243. //int height = readNormalNr(data, it); it+=4;
  244. it+=12;
  245. unsigned int totalBlocks = readNormalNr(data, it);
  246. it+=4;
  247. for (unsigned int i=0; i<256; i++)
  248. {
  249. colors[i].R = data[it++];
  250. colors[i].G = data[it++];
  251. colors[i].B = data[it++];
  252. colors[i].F = 0;
  253. }
  254. offset.resize(totalBlocks);
  255. offList.insert(datasize);
  256. for (unsigned int i=0; i<totalBlocks; i++)
  257. {
  258. it+=4;
  259. unsigned int totalEntries = readNormalNr(data, it);
  260. it+=12;
  261. //13 bytes for name of every frame in this block - not used, skipping
  262. it+= 13 * totalEntries;
  263. for (unsigned int j=0; j<totalEntries; j++)
  264. {
  265. size_t currOffset = readNormalNr(data, it);
  266. offset[i].push_back(currOffset);
  267. offList.insert(currOffset);
  268. it += 4;
  269. }
  270. }
  271. }
  272. unsigned char * CDefFile::getFrame(size_t frame, size_t group) const
  273. {
  274. if (offset.size() > group)
  275. {
  276. if (offset[group].size() > frame)
  277. {
  278. size_t offs = offset[group][frame];
  279. std::set<size_t>::iterator it = offList.find(offs);
  280. if (it == offList.end() || ++it == offList.end())
  281. tlog0<<"Error: offset not found!\n";
  282. size_t size = *it - offs;
  283. unsigned char * ret = new unsigned char[size];
  284. memcpy(ret, data+offs, size);
  285. return ret;
  286. }
  287. }
  288. return NULL;
  289. }
  290. CDefFile::~CDefFile()
  291. {
  292. delete[] data;
  293. delete[] colors;
  294. }
  295. bool CDefFile::loaded() const
  296. {
  297. return data != NULL;
  298. }
  299. /*************************************************************************
  300. * CAnimation for animations handling, can load part of file if needed *
  301. *************************************************************************/
  302. CAnimation::AnimEntry::AnimEntry():
  303. surf(NULL),
  304. source(0),
  305. refCount(0),
  306. data(NULL),
  307. dataSize(0)
  308. {
  309. }
  310. bool CAnimation::loadFrame(CDefFile * file, size_t frame, size_t group)
  311. {
  312. if (groupSize(group) <= frame)
  313. return false;
  314. AnimEntry &e = entries[group][frame];
  315. if (e.surf || e.data)
  316. {
  317. e.refCount++;
  318. return true;
  319. }
  320. if (e.source & 6)//load frame with SDL_Image
  321. {
  322. int size;
  323. unsigned char * pic = NULL;
  324. std::ostringstream str;
  325. if ( e.source & 2 )
  326. str << name << '#' << (group+1) << '#' << (frame+1); // file#12#34.*
  327. else
  328. str << name << '#' << (frame+1);//file#34.*
  329. pic = spriteh->giveFile(str.str(), FILE_GRAPHICS, &size);
  330. if (pic)
  331. {
  332. if (compressed)
  333. {
  334. e.data = pic;
  335. e.dataSize = size;
  336. }
  337. else
  338. {
  339. e.surf = IMG_Load_RW( SDL_RWFromMem((void*)pic, size), 1);
  340. delete pic;
  341. }
  342. }
  343. }
  344. else if (file && e.source & 1)//try to get image from def
  345. {
  346. if (compressed)
  347. e.data = file->getFrame(frame, group);
  348. else
  349. e.surf = file->loadFrame(frame, group);
  350. }
  351. if (!(e.surf || e.data))
  352. return false;//failed to load
  353. e.refCount++;
  354. return true;
  355. }
  356. bool CAnimation::unloadFrame(size_t frame, size_t group)
  357. {
  358. if (groupSize(group) > frame && entries[group][frame].refCount)
  359. {
  360. AnimEntry &e = entries[group][frame];
  361. if (--e.refCount)//not last ref
  362. return true;
  363. SDL_FreeSurface(e.surf);
  364. delete e.data;
  365. e.surf = NULL;
  366. e.data = NULL;
  367. return true;
  368. }
  369. return false;
  370. }
  371. void CAnimation::decompress(AnimEntry &entry)
  372. {
  373. if (entry.source & 6)//load frame with SDL_Image
  374. entry.surf = IMG_Load_RW( SDL_RWFromMem((void*)entry.data, entry.dataSize), 1);
  375. else if (entry.source & 1)
  376. entry.surf = CDefFile::loadFrame(entry.data, defPalette);
  377. }
  378. void CAnimation::init(CDefFile * file)
  379. {
  380. if (compressed)
  381. defPalette = file->getPalette();
  382. for (size_t group = 0; ; group++)
  383. {
  384. std::vector<AnimEntry> toAdd;
  385. for (size_t frame = 0; ; frame++)
  386. {
  387. unsigned char res=0;
  388. {
  389. std::ostringstream str;
  390. str << name << '#' << (group+1) << '#' << (frame+1); // format: file#12#34.*
  391. if (spriteh->haveFile(str.str()))
  392. res |= 2;
  393. }
  394. if (group == 0)
  395. {
  396. std::ostringstream str;
  397. str << name << '#' << (frame+1);// format: file#34.*
  398. if ( spriteh->haveFile(str.str()))
  399. res |=4;
  400. }
  401. if (file)//we have def too. try to get image from def
  402. {
  403. if (file->haveFrame(frame, group))
  404. res |=1;
  405. }
  406. if (res)
  407. {
  408. toAdd.push_back(AnimEntry());
  409. toAdd.back().source = res;
  410. }
  411. else
  412. break;
  413. }
  414. if (!toAdd.empty())
  415. {
  416. entries.push_back(toAdd);
  417. break;
  418. }
  419. }
  420. }
  421. CDefFile * CAnimation::getFile() const
  422. {
  423. CDefFile * file = new CDefFile(name);
  424. if (!file->loaded())
  425. {
  426. delete file;
  427. return NULL;
  428. }
  429. return file;
  430. }
  431. void CAnimation::printError(size_t frame, size_t group, std::string type) const
  432. {
  433. tlog0 << type <<" error: Request for frame not present in CAnimation!\n"
  434. <<"\tFile name: "<<name<<" Group: "<<group<<" Frame: "<<frame<<"\n";
  435. }
  436. CAnimation::CAnimation(std::string Name, bool Compressed):
  437. name(Name),
  438. compressed(Compressed),
  439. defPalette(NULL)
  440. {
  441. std::transform(name.begin(), name.end(), name.begin(), (int(*)(int))toupper);
  442. int dotPos = name.find_last_of('.');
  443. if ( dotPos != -1 )
  444. name.erase(dotPos);
  445. CDefFile * file = getFile();
  446. init(file);
  447. delete file;
  448. }
  449. CAnimation::CAnimation():
  450. name(""),
  451. compressed(false),
  452. defPalette(NULL)
  453. {
  454. }
  455. CAnimation::~CAnimation()
  456. {
  457. delete defPalette;
  458. for (size_t i = 0; i < entries.size(); i++)
  459. for (size_t j = 0; j < entries.at(i).size(); j++)
  460. {
  461. delete entries[i][j].data;
  462. if (entries[i][j].surf)
  463. SDL_FreeSurface(entries[i][j].surf);
  464. }
  465. }
  466. void CAnimation::add(SDL_Surface * surf, bool shared, size_t group)
  467. {
  468. if (!surf)
  469. return;
  470. if (entries.size() <= group)
  471. entries.resize(group+1);
  472. if (shared)
  473. surf->refcount++;
  474. entries[group].push_back(AnimEntry());
  475. entries[group].back().refCount = 1;
  476. entries[group].back().surf = surf;
  477. }
  478. void CAnimation::purgeCompressed()
  479. {
  480. for (size_t group; group < entries.size(); group++)
  481. for (size_t frame; frame < entries[group].size(); frame++)
  482. if (entries[group][frame].surf)
  483. SDL_FreeSurface(entries[group][frame].surf);
  484. }
  485. SDL_Surface * CAnimation::image(size_t frame)
  486. {
  487. size_t group=0;
  488. for (; group<entries.size() && frame > entries[group].size(); group++)
  489. frame -= entries[group].size();
  490. return image(frame, group);
  491. }
  492. SDL_Surface * CAnimation::image(size_t frame, size_t group)
  493. {
  494. if ( groupSize(group) > frame )
  495. {
  496. AnimEntry &e = entries[group][frame];
  497. if (!e.surf && e.data)
  498. decompress(e);
  499. return e.surf;
  500. }
  501. printError(frame, group, "GetImage");
  502. return NULL;
  503. }
  504. void CAnimation::clear()
  505. {
  506. unload();
  507. entries.clear();
  508. }
  509. void CAnimation::load()
  510. {
  511. CDefFile * file = getFile();
  512. for (size_t group = 0; group<entries.size(); group++)
  513. for (size_t frame = 0; frame<entries[group].size(); frame++)
  514. loadFrame(file, frame, group);
  515. delete file;
  516. }
  517. void CAnimation::unload()
  518. {
  519. for (size_t group = 0; group<entries.size(); group++)
  520. for (size_t frame = 0; frame<entries[group].size(); frame++)
  521. unloadFrame(frame, group);
  522. }
  523. void CAnimation::loadGroup(size_t group)
  524. {
  525. CDefFile * file = getFile();
  526. for (size_t frame = 0; frame<entries[group].size(); frame++)
  527. loadFrame(file, frame, group);
  528. }
  529. void CAnimation::unloadGroup(size_t group)
  530. {
  531. for (size_t frame = 0; frame<entries[group].size(); frame++)
  532. unloadFrame(frame, group);
  533. }
  534. void CAnimation::load(size_t frame, size_t group)
  535. {
  536. CDefFile * file = getFile();
  537. loadFrame(file, frame, group);
  538. delete file;
  539. }
  540. void CAnimation::unload(size_t frame, size_t group)
  541. {
  542. unloadFrame(frame, group);
  543. }
  544. void CAnimation::load(std::vector <std::pair <size_t, size_t> > frames)
  545. {
  546. CDefFile * file = getFile();
  547. for (size_t i=0; i<frames.size(); i++)
  548. loadFrame(file, frames[i].second, frames[i].first);
  549. delete file;
  550. }
  551. void CAnimation::unload(std::vector <std::pair <size_t, size_t> > frames)
  552. {
  553. for (size_t i=0; i<frames.size(); i++)
  554. unloadFrame(frames[i].second, frames[i].first);
  555. }
  556. void CAnimation::fixButtonPos()
  557. {
  558. if ( groupSize(0) > 1 )
  559. std::swap(entries[0][1].surf, entries[0][0].surf);
  560. }
  561. size_t CAnimation::groupSize(size_t group) const
  562. {
  563. if (entries.size() > group)
  564. return entries[group].size();
  565. return 0;
  566. }
  567. size_t CAnimation::size() const
  568. {
  569. size_t ret=0;
  570. for (size_t i=0; i<entries.size(); i++)
  571. {
  572. ret += entries[i].size();
  573. }
  574. return ret;
  575. }