texturethumbnail.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program 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. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "texturethumbnail.h"
  19. #include "hashtemplate.h"
  20. #include "missingtexture.h"
  21. #include "targa.h"
  22. #include "ww3dformat.h"
  23. #include "ddsfile.h"
  24. #include "textureloader.h"
  25. #include "bitmaphandler.h"
  26. #include "ffactory.h"
  27. #include "rawfile.h"
  28. #include "mixfile.h"
  29. #include <windows.h>
  30. DLListClass<ThumbnailManagerClass> ThumbnailManagerClass::ThumbnailManagerList;
  31. static bool message_box_displayed=false;
  32. const char* ThumbFileHeader="THU4";
  33. static void Create_Hash_Name(StringClass& name, const StringClass& thumb_name)
  34. {
  35. name=thumb_name;
  36. int len=name.Get_Length();
  37. WWASSERT(!stricmp(&name[len-4],".tga") || !stricmp(&name[len-4],".dds"));
  38. name[len-4]='\0';
  39. }
  40. /* file_auto_ptr my_tga_file(_TheFileFactory,filename);
  41. if (my_tga_file->Is_Available()) {
  42. my_tga_file->Open();
  43. unsigned size=my_tga_file->Size();
  44. char* tga_memory=new char[size];
  45. my_tga_file->Read(tga_memory,size);
  46. my_tga_file->Close();
  47. StringClass pth("data\\");
  48. pth+=filename;
  49. RawFileClass tmp_tga_file(pth);
  50. tmp_tga_file.Create();
  51. tmp_tga_file.Write(tga_memory,size);
  52. tmp_tga_file.Close();
  53. delete[] tga_memory;
  54. }
  55. */
  56. ThumbnailClass::ThumbnailClass(
  57. ThumbnailManagerClass* manager,
  58. const char* name,
  59. unsigned char* bitmap,
  60. unsigned w,
  61. unsigned h,
  62. unsigned original_w,
  63. unsigned original_h,
  64. unsigned original_mip_level_count,
  65. WW3DFormat original_format,
  66. bool allocated,
  67. unsigned long date_time)
  68. :
  69. Manager(manager),
  70. Name(name),
  71. Bitmap(bitmap),
  72. Allocated(allocated),
  73. Width(w),
  74. Height(h),
  75. OriginalTextureWidth(original_w),
  76. OriginalTextureHeight(original_h),
  77. OriginalTextureMipLevelCount(original_mip_level_count),
  78. OriginalTextureFormat(original_format),
  79. DateTime(date_time)
  80. {
  81. Manager->Insert_To_Hash(this);
  82. }
  83. // ----------------------------------------------------------------------------
  84. //
  85. // Load texture and generate mipmap levels if requested. The function tries
  86. // to create texture that matches targa format. If suitable format is not
  87. // available, it selects closest matching format and performs color space
  88. // conversion.
  89. //
  90. // ----------------------------------------------------------------------------
  91. ThumbnailClass::ThumbnailClass(ThumbnailManagerClass* manager, const StringClass& filename)
  92. :
  93. Manager(manager),
  94. Bitmap(0),
  95. Name(filename),
  96. Allocated(false),
  97. Width(0),
  98. Height(0),
  99. OriginalTextureWidth(0),
  100. OriginalTextureHeight(0),
  101. OriginalTextureMipLevelCount(0),
  102. OriginalTextureFormat(WW3D_FORMAT_UNKNOWN),
  103. DateTime(0)
  104. {
  105. unsigned reduction_factor=3;
  106. // First, try loading image from a DDS file
  107. DDSFileClass dds_file(filename,reduction_factor);
  108. if (dds_file.Is_Available() && dds_file.Load()) {
  109. DateTime=dds_file.Get_Date_Time();
  110. int len=Name.Get_Length();
  111. WWASSERT(len>4);
  112. Name[len-3]='d';
  113. Name[len-2]='d';
  114. Name[len-1]='s';
  115. unsigned level=0;
  116. while (dds_file.Get_Width(level)>32 || dds_file.Get_Height(level)>32) {
  117. if (level>=dds_file.Get_Mip_Level_Count()) break;
  118. level++;
  119. }
  120. OriginalTextureWidth=dds_file.Get_Full_Width();
  121. OriginalTextureHeight=dds_file.Get_Full_Height();
  122. OriginalTextureFormat=dds_file.Get_Format();
  123. OriginalTextureMipLevelCount=dds_file.Get_Mip_Level_Count();
  124. Width=dds_file.Get_Width(0);
  125. Height=dds_file.Get_Height(0);
  126. Bitmap=new unsigned char[Width*Height*2];
  127. Allocated=true;
  128. dds_file.Copy_Level_To_Surface(
  129. 0, // Level
  130. WW3D_FORMAT_A4R4G4B4,
  131. Width,
  132. Height,
  133. Bitmap,
  134. Width*2);
  135. }
  136. // If DDS file can't be used try loading from TGA
  137. else {
  138. // Make sure the file can be opened. If not, return missing texture.
  139. Targa targa;
  140. if (TARGA_ERROR_HANDLER(targa.Open(filename,TGA_READMODE),filename)) return;
  141. // DX8 uses image upside down compared to TGA
  142. targa.Header.ImageDescriptor ^= TGAIDF_YORIGIN;
  143. WW3DFormat src_format,dest_format;
  144. unsigned src_bpp=0;
  145. Get_WW3D_Format(src_format,src_bpp,targa);
  146. if (src_format==WW3D_FORMAT_UNKNOWN) {
  147. WWDEBUG_SAY(("Unknown texture format for %s\n",filename));
  148. return;
  149. }
  150. // Destination size will be the next power of two square from the larger width and height...
  151. OriginalTextureWidth=targa.Header.Width;
  152. OriginalTextureHeight=targa.Header.Height;
  153. OriginalTextureFormat=src_format;
  154. Width=targa.Header.Width>>reduction_factor;
  155. Height=targa.Header.Height>>reduction_factor;
  156. OriginalTextureMipLevelCount=1;
  157. unsigned iw=1;
  158. unsigned ih=1;
  159. while (iw<OriginalTextureWidth && ih<OriginalTextureHeight) {
  160. iw+=iw;
  161. ih+=ih;
  162. OriginalTextureMipLevelCount++;
  163. }
  164. while (Width>32 || Height>32) {
  165. reduction_factor++;
  166. Width>>=2;
  167. Height>>=2;
  168. }
  169. unsigned poweroftwowidth = 1;
  170. while (poweroftwowidth < Width) {
  171. poweroftwowidth <<= 1;
  172. }
  173. unsigned poweroftwoheight = 1;
  174. while (poweroftwoheight < Height) {
  175. poweroftwoheight <<= 1;
  176. }
  177. Width=poweroftwowidth;
  178. Height=poweroftwoheight;
  179. unsigned src_width=targa.Header.Width;
  180. unsigned src_height=targa.Header.Height;
  181. // NOTE: We load the palette but we do not yet support paletted textures!
  182. char palette[256*4];
  183. targa.SetPalette(palette);
  184. if (TARGA_ERROR_HANDLER(targa.Load(filename, TGAF_IMAGE, false),filename)) return;
  185. // Get time stamp from the tga file
  186. {
  187. file_auto_ptr my_tga_file(_TheFileFactory,filename);
  188. WWASSERT(my_tga_file->Is_Available());
  189. my_tga_file->Open();
  190. DateTime=my_tga_file->Get_Date_Time();
  191. my_tga_file->Close();
  192. }
  193. unsigned char* src_surface=(unsigned char*)targa.GetImage();
  194. int len=Name.Get_Length();
  195. WWASSERT(len>4);
  196. Name[len-3]='t';
  197. Name[len-2]='g';
  198. Name[len-1]='a';
  199. Bitmap=new unsigned char[Width*Height*2];
  200. Allocated=true;
  201. dest_format=WW3D_FORMAT_A8R8G8B8;
  202. BitmapHandlerClass::Copy_Image(
  203. Bitmap,
  204. Width,
  205. Height,
  206. Width*2,
  207. WW3D_FORMAT_A4R4G4B4,
  208. src_surface,
  209. src_width,
  210. src_height,
  211. src_width*src_bpp,
  212. src_format,
  213. (unsigned char*)targa.GetPalette(),
  214. targa.Header.CMapDepth>>3,
  215. false);
  216. }
  217. Manager->Insert_To_Hash(this);
  218. }
  219. ThumbnailClass::~ThumbnailClass()
  220. {
  221. if (Allocated) delete[] Bitmap;
  222. Manager->Remove_From_Hash(this);
  223. }
  224. void ThumbnailManagerClass::Create_Thumbnails()
  225. {
  226. SimpleFileFactoryClass ff;
  227. ff.Set_Sub_Directory("Data\\");
  228. MixFileFactoryClass mix(MixFileName, &ff);
  229. FileFactoryClass* old_file_factory=_TheFileFactory;
  230. _TheFileFactory=&mix;
  231. if (mix.Is_Valid()) {
  232. DynamicVectorClass<StringClass> list;
  233. list.Set_Growth_Step (1000);
  234. mix.Build_Filename_List(list);
  235. for (int i=0;i<list.Count();++i) {
  236. int len=list[i].Get_Length();
  237. if (!stricmp(&list[i][len-4],".tga") || !stricmp(&list[i][len-4],".dds")) {
  238. if (!Peek_Thumbnail_Instance(list[i])) {
  239. new ThumbnailClass(this,list[i]);
  240. }
  241. }
  242. }
  243. }
  244. _TheFileFactory=old_file_factory;
  245. }
  246. void ThumbnailManagerClass::Load()
  247. {
  248. WWASSERT(!ThumbnailMemory);
  249. // If the thumbnail hash table file is available, init hash table
  250. DateTime=0;
  251. file_auto_ptr thumb_file(_TheFileFactory, ThumbnailFileName);
  252. if (thumb_file->Is_Available()) {
  253. thumb_file->Open(FileClass::READ);
  254. DateTime=thumb_file->Get_Date_Time();
  255. char tmp[4];
  256. thumb_file->Read(tmp,4);
  257. if (tmp[0]==ThumbFileHeader[0] &&
  258. tmp[1]==ThumbFileHeader[1] &&
  259. tmp[2]==ThumbFileHeader[2] &&
  260. tmp[3]==ThumbFileHeader[3]) {
  261. int total_thumb_count;
  262. int total_header_length;
  263. int total_data_length;
  264. thumb_file->Read(&total_thumb_count,sizeof(int));
  265. thumb_file->Read(&total_header_length,sizeof(int));
  266. thumb_file->Read(&total_data_length,sizeof(int));
  267. if (total_thumb_count) {
  268. WWASSERT(total_data_length && total_header_length);
  269. ThumbnailMemory=new unsigned char[total_data_length];
  270. // Load thumbs
  271. for (int i=0;i<total_thumb_count;++i) {
  272. char name[256];
  273. int offset;
  274. int width;
  275. int height;
  276. int original_width;
  277. int original_height;
  278. int original_mip_level_count;
  279. WW3DFormat original_format;
  280. int name_len;
  281. unsigned long date_time;
  282. thumb_file->Read(&date_time,sizeof(unsigned long));
  283. thumb_file->Read(&offset,sizeof(int));
  284. thumb_file->Read(&width,sizeof(int));
  285. thumb_file->Read(&height,sizeof(int));
  286. thumb_file->Read(&original_width,sizeof(int));
  287. thumb_file->Read(&original_height,sizeof(int));
  288. thumb_file->Read(&original_mip_level_count,sizeof(int));
  289. thumb_file->Read(&original_format,sizeof(int));
  290. thumb_file->Read(&name_len,sizeof(int));
  291. WWASSERT(name_len<255);
  292. thumb_file->Read(name,name_len);
  293. name[name_len]='\0';
  294. // If per-texture time stamp test is enabled, thumbnail is only used if its time stamp
  295. // matches the texture's time stamp.
  296. bool valid=true;
  297. if (Is_Per_Texture_Time_Stamp_Used()) {
  298. file_auto_ptr texture_file(_TheFileFactory, name);
  299. if (texture_file->Is_Available()) {
  300. texture_file->Open();
  301. if (texture_file->Get_Date_Time()!=date_time) {
  302. valid=false;
  303. }
  304. texture_file->Close();
  305. }
  306. else {
  307. valid=false;
  308. }
  309. }
  310. if (valid) {
  311. new ThumbnailClass(
  312. this,
  313. name,
  314. ThumbnailMemory+offset-total_header_length,
  315. width,
  316. height,
  317. original_width,
  318. original_height,
  319. original_mip_level_count,
  320. original_format,
  321. false,
  322. date_time);
  323. }
  324. }
  325. thumb_file->Read(ThumbnailMemory,total_data_length);
  326. }
  327. }
  328. thumb_file->Close();
  329. }
  330. Changed=false;
  331. }
  332. void ThumbnailManagerClass::Save(bool force)
  333. {
  334. // Save only if changed
  335. if (!Changed && !force) return;
  336. Changed=false;
  337. // If the thumbnail hash table was modified, save it to disk
  338. HashTemplateIterator<StringClass,ThumbnailClass*> ite(ThumbnailHash);
  339. int total_header_length=0;
  340. int total_data_length=0;
  341. int total_thumb_count=0;
  342. total_header_length+=4; // header
  343. total_header_length+=4; // thumb count
  344. total_header_length+=4; // header size
  345. total_header_length+=4; // data length
  346. for (ite.First();!ite.Is_Done();ite.Next()) {
  347. ThumbnailClass* thumb=ite.Peek_Value();
  348. total_header_length+=4; // per-thumb date-time
  349. total_header_length+=4; // int bitmap offset
  350. total_header_length+=4; // int bitmap width
  351. total_header_length+=4; // int bitmap height
  352. total_header_length+=4; // int original bitmap width
  353. total_header_length+=4; // int original bitmap height
  354. total_header_length+=4; // int original mip level count
  355. total_header_length+=4; // int original format
  356. total_header_length+=4; // int name string length
  357. total_header_length+=strlen(thumb->Get_Name());
  358. total_data_length+=thumb->Get_Width()*thumb->Get_Height()*2;
  359. total_thumb_count++;
  360. }
  361. int offset=total_header_length;
  362. file_auto_ptr thumb_file(_TheWritingFileFactory, ThumbnailFileName);
  363. if (thumb_file->Is_Available()) {
  364. thumb_file->Delete();
  365. }
  366. thumb_file->Create();
  367. thumb_file->Open(FileClass::WRITE);
  368. thumb_file->Write(ThumbFileHeader,4);
  369. thumb_file->Write(&total_thumb_count,sizeof(int));
  370. thumb_file->Write(&total_header_length,sizeof(int));
  371. thumb_file->Write(&total_data_length,sizeof(int));
  372. // Save names and offsets
  373. for (ite.First();!ite.Is_Done();ite.Next()) {
  374. ThumbnailClass* thumb=ite.Peek_Value();
  375. const char* name=thumb->Get_Name();
  376. int name_len=strlen(name);
  377. int width=thumb->Get_Width();
  378. int height=thumb->Get_Height();
  379. int original_width=thumb->Get_Original_Texture_Width();
  380. int original_height=thumb->Get_Original_Texture_Height();
  381. int original_mip_level_count=thumb->Get_Original_Texture_Mip_Level_Count();
  382. WW3DFormat original_format=thumb->Get_Original_Texture_Format();
  383. unsigned long date_time=thumb->Get_Date_Time();
  384. thumb_file->Write(&date_time,sizeof(unsigned long));
  385. thumb_file->Write(&offset,sizeof(int));
  386. thumb_file->Write(&width,sizeof(int));
  387. thumb_file->Write(&height,sizeof(int));
  388. thumb_file->Write(&original_width,sizeof(int));
  389. thumb_file->Write(&original_height,sizeof(int));
  390. thumb_file->Write(&original_mip_level_count,sizeof(int));
  391. thumb_file->Write(&original_format,sizeof(int));
  392. thumb_file->Write(&name_len,sizeof(int));
  393. thumb_file->Write(name,name_len);
  394. offset+=width*height*2;
  395. }
  396. // Save bitmaps
  397. offset=total_header_length;
  398. for (ite.First();!ite.Is_Done();ite.Next()) {
  399. ThumbnailClass* thumb=ite.Peek_Value();
  400. int width=thumb->Get_Width();
  401. int height=thumb->Get_Height();
  402. thumb_file->Write(thumb->Peek_Bitmap(),width*height*2);
  403. }
  404. if (DateTime) thumb_file->Set_Date_Time(DateTime);
  405. thumb_file->Close();
  406. }
  407. // ----------------------------------------------------------------------------
  408. ThumbnailManagerClass::ThumbnailManagerClass(const char* thumbnail_filename, const char* mix_filename)
  409. :
  410. ThumbnailMemory(NULL),
  411. ThumbnailFileName(thumbnail_filename),
  412. MixFileName(mix_filename),
  413. PerTextureTimeStampUsed(false),
  414. CreateThumbnailIfNotFound(false),
  415. Changed(false),
  416. DateTime(0)
  417. {
  418. Load();
  419. }
  420. // ----------------------------------------------------------------------------
  421. ThumbnailManagerClass::~ThumbnailManagerClass()
  422. {
  423. Save();
  424. HashTemplateIterator<StringClass,ThumbnailClass*> ite(ThumbnailHash);
  425. ite.First();
  426. while (!ite.Is_Done()) {
  427. ThumbnailClass* thumb=ite.Peek_Value();
  428. delete thumb;
  429. ite.First();
  430. }
  431. if (ThumbnailMemory) delete[] ThumbnailMemory;
  432. ThumbnailMemory=NULL;
  433. }
  434. // ----------------------------------------------------------------------------
  435. void ThumbnailManagerClass::Add_Thumbnail_Manager(const char* thumbnail_filename, const char* mix_filename)
  436. {
  437. // First loop over all thumbnail managers to see if we already have this one created. This isn't
  438. // supposed to be called often at all and there are usually just couple managers alive,
  439. // so we'll do pure string compares here...
  440. ThumbnailManagerClass* man=ThumbnailManagerList.Head();
  441. while (man) {
  442. if (man->ThumbnailFileName==thumbnail_filename) return;
  443. man=man->Succ();
  444. }
  445. // Always update thumbnail files when they're out of date
  446. Update_Thumbnail_File(mix_filename,false);
  447. // Not found, create and add to the list.
  448. man=new ThumbnailManagerClass(thumbnail_filename,mix_filename);
  449. ThumbnailManagerList.Add_Tail(man);
  450. }
  451. // ----------------------------------------------------------------------------
  452. void ThumbnailManagerClass::Remove_Thumbnail_Manager(const char* thumbnail_filename)
  453. {
  454. ThumbnailManagerClass* man=ThumbnailManagerList.Head();
  455. while (man) {
  456. if (man->ThumbnailFileName==thumbnail_filename) {
  457. delete man;
  458. return;
  459. }
  460. man=man->Succ();
  461. }
  462. }
  463. // ----------------------------------------------------------------------------
  464. ThumbnailClass* ThumbnailManagerClass::Peek_Thumbnail_Instance(const StringClass& name)
  465. {
  466. ThumbnailClass* thumb=Get_From_Hash(name);
  467. if (!thumb) {
  468. if (Is_Thumbnail_Created_If_Not_Found()) {
  469. thumb=new ThumbnailClass(this,name);
  470. if (!thumb->Peek_Bitmap()) {
  471. delete thumb;
  472. thumb=NULL;
  473. }
  474. }
  475. }
  476. return thumb;
  477. }
  478. void ThumbnailManagerClass::Insert_To_Hash(ThumbnailClass* thumb)
  479. {
  480. Changed=true;
  481. StringClass hash_name(0,true);
  482. Create_Hash_Name(hash_name,thumb->Get_Name());
  483. ThumbnailHash.Insert(hash_name,thumb);
  484. }
  485. ThumbnailClass* ThumbnailManagerClass::Get_From_Hash(const StringClass& name)
  486. {
  487. StringClass hash_name(0,true);
  488. Create_Hash_Name(hash_name,name);
  489. return ThumbnailHash.Get(hash_name);
  490. }
  491. void ThumbnailManagerClass::Remove_From_Hash(ThumbnailClass* thumb)
  492. {
  493. Changed=true;
  494. StringClass hash_name(0,true);
  495. Create_Hash_Name(hash_name,thumb->Get_Name());
  496. ThumbnailHash.Remove(hash_name);
  497. }
  498. void ThumbnailManagerClass::Update_Thumbnail_File(const char* mix_file_name,bool display_message_box)
  499. {
  500. SimpleFileFactoryClass ff;
  501. ff.Set_Sub_Directory("Data\\");
  502. StringClass thumb_file_name(mix_file_name,true);
  503. int len=thumb_file_name.Get_Length();
  504. WWASSERT(len>4);
  505. thumb_file_name[len-3]='t';
  506. thumb_file_name[len-2]='h';
  507. thumb_file_name[len-1]='u';
  508. FileClass* mix_file=ff.Get_File(mix_file_name);
  509. FileClass* thumb_file=ff.Get_File(thumb_file_name);
  510. WWASSERT(mix_file && thumb_file);
  511. // If mix file isn't found but thumb file is, delete the obsolete thumb file
  512. mix_file->Open(FileClass::READ);
  513. thumb_file->Open(FileClass::READ); //|FileClass::WRITE); Changed this to READ only since we never use the handle for writing and it may cause contention amongst slave servers ST - 12/14/2001 8:26PM
  514. if (!mix_file->Is_Available()) {
  515. if (thumb_file->Is_Available()) {
  516. thumb_file->Delete();
  517. }
  518. mix_file->Close();
  519. thumb_file->Close();
  520. ff.Return_File(mix_file);
  521. ff.Return_File(thumb_file);
  522. return;
  523. }
  524. unsigned long mix_date_time=mix_file->Get_Date_Time();
  525. if (thumb_file->Is_Available()) {
  526. unsigned long thumb_date_time=thumb_file->Get_Date_Time();
  527. if (mix_date_time!=thumb_date_time) {
  528. thumb_file->Delete();
  529. }
  530. // Read header to make sure the thumb file is of correct version.
  531. char tmp[4];
  532. thumb_file->Read(tmp,4);
  533. if (tmp[0]!=ThumbFileHeader[0] ||
  534. tmp[1]!=ThumbFileHeader[1] ||
  535. tmp[2]!=ThumbFileHeader[2] ||
  536. tmp[3]!=ThumbFileHeader[3]) {
  537. thumb_file->Delete();
  538. }
  539. }
  540. if (thumb_file->Is_Available()) {
  541. mix_file->Close();
  542. thumb_file->Close();
  543. ff.Return_File(mix_file);
  544. ff.Return_File(thumb_file);
  545. return;
  546. }
  547. if (display_message_box && !message_box_displayed) {
  548. message_box_displayed=true;
  549. ::MessageBox(NULL,
  550. "Some or all texture thumbnails need to be updated.\n"
  551. "This will take a while. The update will only be done once\n"
  552. "each time a mix file changes and thumb database hasn't been\n"
  553. "updated.",
  554. "Updating texture thumbnails",
  555. MB_OK);
  556. }
  557. // we don't currently have a thumbnail file (either we just deleted it or it never existed, we don't care)
  558. // so we must create one now.
  559. ThumbnailManagerClass* manager=new ThumbnailManagerClass(thumb_file_name, mix_file_name);
  560. manager->DateTime=mix_date_time; // Set the datetime to mixfile's datetime.
  561. manager->Create_Thumbnails();
  562. manager->Save(true);
  563. delete manager;
  564. // close files and return pointers
  565. mix_file->Close();
  566. thumb_file->Close();
  567. ff.Return_File(mix_file);
  568. ff.Return_File(thumb_file);
  569. }
  570. // Verify that up-to-date thumbnails exist for all textures
  571. void ThumbnailManagerClass::Pre_Init(bool display_message_box)
  572. {
  573. WWASSERT(!ThumbnailManagerList.Head());
  574. // Collect all mix file names
  575. DynamicVectorClass<StringClass> mix_names;
  576. char cur_dir[256];
  577. GetCurrentDirectory(sizeof(cur_dir),cur_dir);
  578. StringClass new_dir(cur_dir,true);
  579. new_dir+="\\Data";
  580. SetCurrentDirectory(new_dir);
  581. WIN32_FIND_DATA find_data;
  582. HANDLE handle=FindFirstFile("*.mix",&find_data);
  583. if (handle!=INVALID_HANDLE_VALUE) {
  584. for (;;) {
  585. if (!(find_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
  586. mix_names.Add(find_data.cFileName);
  587. }
  588. if (!FindNextFile(handle,&find_data)) {
  589. FindClose(handle);
  590. break;
  591. }
  592. }
  593. }
  594. SetCurrentDirectory(cur_dir);
  595. // First generate thumbnails for always.dat
  596. Update_Thumbnail_File("always.dat",display_message_box);
  597. // Then loop over all .mix files
  598. for (int i=0;i<mix_names.Count();++i) {
  599. Update_Thumbnail_File(mix_names[i],display_message_box);
  600. }
  601. }
  602. void ThumbnailManagerClass::Init()
  603. {
  604. // Renegade uses always.dat... Should all our games use, or should this be moved to somewhere else?
  605. Add_Thumbnail_Manager("always.thu","always.dat");
  606. Add_Thumbnail_Manager("always2.thu","always2.dat");
  607. }
  608. void ThumbnailManagerClass::Deinit()
  609. {
  610. while (ThumbnailManagerClass* man=ThumbnailManagerList.Head()) {
  611. delete man;
  612. }
  613. }