texturethumbnail.cpp 22 KB

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