Glide Plugin GLES2 port from mupen64plus-ae, but with special FrameSkip code
[mupen64plus-pandora.git] / source / gles2glide64 / src / GlideHQ / TxHiResCache.cpp
1 /*
2  * Texture Filtering
3  * Version:  1.0
4  *
5  * Copyright (C) 2007  Hiroshi Morii   All Rights Reserved.
6  * Email koolsmoky(at)users.sourceforge.net
7  * Web   http://www.3dfxzone.it/koolsmoky
8  *
9  * this is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2, or (at your option)
12  * any later version.
13  *
14  * this is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with GNU Make; see the file COPYING.  If not, write to
21  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 /* 2007 Gonetz <gonetz(at)ngs.ru>
25  * Added callback to display hires texture info. */
26
27 #ifdef __MSC__
28 #pragma warning(disable: 4786)
29 #endif
30
31 /* handle oversized textures by
32  *   0: minification
33  *   1: Glide64 style tiling
34  */
35 #define TEXTURE_TILING 1
36
37 /* use power of 2 texture size
38  * (0:disable, 1:enable, 2:3dfx) */
39 #define POW2_TEXTURES 2
40
41 #if TEXTURE_TILING
42 #undef POW2_TEXTURES
43 #define POW2_TEXTURES 2
44 #endif
45
46 /* hack to reduce texture footprint to achieve
47  * better performace on midrange gfx cards.
48  * (0:disable, 1:enable) */
49 #define REDUCE_TEXTURE_FOOTPRINT 0
50
51 /* use aggressive format assumption for quantization
52  * (0:disable, 1:enable, 2:extreme) */
53 #define AGGRESSIVE_QUANTIZATION 1
54
55 #include <zlib.h>
56 #include <string>
57 #include <SDL.h>
58 #include "TxHiResCache.h"
59 #include "TxDbg.h"
60 #include "../Glide64/Gfx_1.3.h"
61
62 TxHiResCache::~TxHiResCache()
63 {
64 #ifdef DUMP_CACHE
65   if ((_options & DUMP_HIRESTEXCACHE) && !_haveCache && !_abortLoad) {
66     /* dump cache to disk */
67     std::wstring filename = _ident + L"_HIRESTEXTURES.dat";
68     boost::filesystem::wpath cachepath(_cachepath);
69     cachepath /= boost::filesystem::wpath(L"glidehq");
70     int config = _options & (HIRESTEXTURES_MASK|COMPRESS_HIRESTEX|COMPRESSION_MASK|TILE_HIRESTEX|FORCE16BPP_HIRESTEX|GZ_HIRESTEXCACHE|LET_TEXARTISTS_FLY);
71
72     TxCache::save(cachepath.wstring().c_str(), filename.c_str(), config);
73   }
74 #endif
75
76   delete _txImage;
77   delete _txQuantize;
78   delete _txReSample;
79 }
80
81 TxHiResCache::TxHiResCache(int maxwidth, int maxheight, int maxbpp, int options,
82                            const wchar_t *datapath, const wchar_t *cachepath,
83                            const wchar_t *ident, dispInfoFuncExt callback
84                            ) : TxCache((options & ~GZ_TEXCACHE), 0, datapath, cachepath, ident, callback)
85 {
86   _txImage = new TxImage();
87   _txQuantize  = new TxQuantize();
88   _txReSample = new TxReSample();
89
90   _maxwidth  = maxwidth;
91   _maxheight = maxheight;
92   _maxbpp    = maxbpp;
93   _abortLoad = 0;
94   _haveCache = 0;
95
96   /* assert local options */
97   if (!(_options & COMPRESS_HIRESTEX))
98     _options &= ~COMPRESSION_MASK;
99
100   if (_cachepath.empty() || _ident.empty()) {
101     _options &= ~DUMP_HIRESTEXCACHE;
102     return;
103   }
104
105 #ifdef DUMP_CACHE
106   /* read in hires texture cache */
107   if (_options & DUMP_HIRESTEXCACHE) {
108     /* find it on disk */
109     std::wstring filename = _ident + L"_HIRESTEXTURES.dat";
110     boost::filesystem::wpath cachepath(_cachepath);
111     cachepath /= boost::filesystem::wpath(L"glidehq");
112     int config = _options & (HIRESTEXTURES_MASK|COMPRESS_HIRESTEX|COMPRESSION_MASK|TILE_HIRESTEX|FORCE16BPP_HIRESTEX|GZ_HIRESTEXCACHE|LET_TEXARTISTS_FLY);
113
114     _haveCache = TxCache::load(cachepath.wstring().c_str(), filename.c_str(), config);
115   }
116 #endif
117
118   /* read in hires textures */
119   if (!_haveCache) TxHiResCache::load(0);
120 }
121
122 boolean
123 TxHiResCache::empty()
124 {
125   return _cache.empty();
126 }
127
128 boolean
129 TxHiResCache::load(boolean replace) /* 0 : reload, 1 : replace partial */
130 {
131   if (!_datapath.empty() && !_ident.empty()) {
132
133     if (!replace) TxCache::clear();
134
135     boost::filesystem::wpath dir_path(_datapath);
136
137     switch (_options & HIRESTEXTURES_MASK) {
138     case GHQ_HIRESTEXTURES:
139       break;
140     case RICE_HIRESTEXTURES:
141       INFO(80, L"-----\n");
142       INFO(80, L"using Rice hires texture format...\n");
143       INFO(80, L"  must be one of the following;\n");
144       INFO(80, L"    1) *_rgb.png + *_a.png\n");
145       INFO(80, L"    2) *_all.png\n");
146       INFO(80, L"    3) *_ciByRGBA.png\n");
147       INFO(80, L"    4) *_allciByRGBA.png\n");
148       INFO(80, L"    5) *_ci.bmp\n");
149       INFO(80, L"  usage of only 2) and 3) highly recommended!\n");
150       INFO(80, L"  folder names must be in US-ASCII characters!\n");
151
152       dir_path /= boost::filesystem::wpath(L"hires_texture");
153       dir_path /= boost::filesystem::wpath(_ident);
154       loadHiResTextures(dir_path, replace);
155       break;
156     case JABO_HIRESTEXTURES:
157       ;
158     }
159
160     return 1;
161   }
162
163   return 0;
164 }
165
166 boolean
167 TxHiResCache::loadHiResTextures(boost::filesystem::wpath dir_path, boolean replace)
168 {
169   uint32_t last, now, diff;
170   DBG_INFO(80, L"-----\n");
171   DBG_INFO(80, L"path: %ls\n", dir_path.string().c_str());
172   last = SDL_GetTicks();
173
174   /* find it on disk */
175   if (!boost::filesystem::exists(dir_path)) {
176     INFO(80, L"Error: path not found!\n");
177     return 0;
178   }
179
180   /* XXX: deal with UNICODE fiasco!
181    * stupidity flows forth beneath this...
182    *
183    * I opted to use chdir in order to use fopen() for windows 9x.
184    */
185 #ifdef WIN32
186   wchar_t curpath[MAX_PATH];
187   GETCWD(MAX_PATH, curpath);
188   CHDIR(dir_path.wstring().c_str());
189 #else
190   char curpath[MAX_PATH];
191   char cbuf[MAX_PATH];
192   wcstombs(cbuf, dir_path.wstring().c_str(), MAX_PATH);
193   if (GETCWD(MAX_PATH, curpath) == NULL)
194       ERRLOG("Error while retrieving working directory!");
195   if (CHDIR(cbuf) != 0)
196       ERRLOG("Error while changing current directory to '%s'!", cbuf);
197 #endif
198
199   /* NOTE: I could use the boost::wdirectory_iterator and boost::wpath
200    * to resolve UNICODE file names and paths. But then, _wfopen() is
201    * required to get the file descriptor for MS Windows to pass into
202    * libpng, which is incompatible with Win9x. Win9x's fopen() cannot
203    * handle UNICODE names. UNICODE capable boost::filesystem is available
204    * with Boost1.34.1 built with VC8.0 (bjam --toolset=msvc-8.0 stage).
205    *
206    * RULE OF THUMB: NEVER save texture packs in NON-ASCII names!!
207    */
208   boost::filesystem::directory_iterator it(dir_path);
209   boost::filesystem::directory_iterator end_it; /* default construction yields past-the-end */
210
211   for (; it != end_it; ++it) {
212
213     if (KBHIT(0x1B)) {
214       _abortLoad = 1;
215       if (_callback) (*_callback)(L"Aborted loading hiresolution texture!\n");
216       INFO(80, L"Error: aborted loading hiresolution texture!\n");
217     }
218     if (_abortLoad) break;
219
220     /* recursive read into sub-directory */
221     if (boost::filesystem::is_directory(it->status())) {
222       loadHiResTextures(it->path(), replace);
223       continue;
224     }
225
226     DBG_INFO(80, L"-----\n");
227     DBG_INFO(80, L"file: %ls\n", it->path().leaf().c_str());
228
229     int width = 0, height = 0;
230     uint16 format = 0;
231     uint8 *tex = NULL;
232     int tmpwidth = 0, tmpheight = 0;
233     uint16 tmpformat = 0;
234     uint8 *tmptex= NULL;
235     int untiled_width = 0, untiled_height = 0;
236     uint16 destformat = 0;
237
238     /* Rice hi-res textures: begin
239      */
240     uint32 chksum = 0, fmt = 0, siz = 0, palchksum = 0;
241     char *pfname = NULL, fname[MAX_PATH];
242     std::string ident;
243     FILE *fp = NULL;
244
245     wcstombs(fname, _ident.c_str(), MAX_PATH);
246     /* XXX case sensitivity fiasco!
247      * files must use _a, _rgb, _all, _allciByRGBA, _ciByRGBA, _ci
248      * and file extensions must be in lower case letters! */
249 #ifdef WIN32
250     {
251       unsigned int i;
252       for (i = 0; i < strlen(fname); i++) fname[i] = tolower(fname[i]);
253     }
254 #endif
255     ident.assign(fname);
256
257     /* read in Rice's file naming convention */
258 #define CRCFMTSIZ_LEN 13
259 #define PALCRC_LEN 9
260     //wcstombs(fname, it->path().leaf().c_str(), MAX_PATH);
261     strncpy(fname, it->path().leaf().string().c_str(), sizeof(fname));
262     fname[sizeof(fname) - 1] = '\0';
263     /* XXX case sensitivity fiasco!
264      * files must use _a, _rgb, _all, _allciByRGBA, _ciByRGBA, _ci
265      * and file extensions must be in lower case letters! */
266 #ifdef WIN32
267     {
268       unsigned int i;
269       for (i = 0; i < strlen(fname); i++) fname[i] = tolower(fname[i]);
270     }
271 #endif
272     pfname = fname + strlen(fname) - 4;
273     if (!(pfname == strstr(fname, ".png") ||
274           pfname == strstr(fname, ".bmp") ||
275           pfname == strstr(fname, ".dds"))) {
276 #if !DEBUG
277       INFO(80, L"-----\n");
278       INFO(80, L"path: %ls\n", dir_path.string().c_str());
279       INFO(80, L"file: %ls\n", it->path().leaf().c_str());
280 #endif
281       INFO(80, L"Error: not png or bmp or dds!\n");
282       continue;
283     }
284     pfname = strstr(fname, ident.c_str());
285     if (pfname != fname) pfname = 0;
286     if (pfname) {
287       if (sscanf(pfname + ident.size(), "#%08X#%01X#%01X#%08X", &chksum, &fmt, &siz, &palchksum) == 4)
288         pfname += (ident.size() + CRCFMTSIZ_LEN + PALCRC_LEN);
289       else if (sscanf(pfname + ident.size(), "#%08X#%01X#%01X", &chksum, &fmt, &siz) == 3)
290         pfname += (ident.size() + CRCFMTSIZ_LEN);
291       else
292         pfname = 0;
293     }
294     if (!pfname) {
295 #if !DEBUG
296       INFO(80, L"-----\n");
297       INFO(80, L"path: %ls\n", dir_path.string().c_str());
298       INFO(80, L"file: %ls\n", it->path().leaf().c_str());
299 #endif
300       INFO(80, L"Error: not Rice texture naming convention!\n");
301       continue;
302     }
303     if (!chksum) {
304 #if !DEBUG
305       INFO(80, L"-----\n");
306       INFO(80, L"path: %ls\n", dir_path.string().c_str());
307       INFO(80, L"file: %ls\n", it->path().leaf().c_str());
308 #endif
309       INFO(80, L"Error: crc32 = 0!\n");
310       continue;
311     }
312
313     /* check if we already have it in hires texture cache */
314     if (!replace) {
315       uint64 chksum64 = (uint64)palchksum;
316       chksum64 <<= 32;
317       chksum64 |= (uint64)chksum;
318       if (TxCache::is_cached(chksum64)) {
319 #if !DEBUG
320         INFO(80, L"-----\n");
321         INFO(80, L"path: %ls\n", dir_path.string().c_str());
322         INFO(80, L"file: %ls\n", it->path().leaf().c_str());
323 #endif
324         INFO(80, L"Error: already cached! duplicate texture!\n");
325         continue;
326       }
327     }
328
329     DBG_INFO(80, L"rom: %ls chksum:%08X %08X fmt:%x size:%x\n", _ident.c_str(), chksum, palchksum, fmt, siz);
330
331     /* Deal with the wackiness some texture packs utilize Rice format.
332      * Read in the following order: _a.* + _rgb.*, _all.png _ciByRGBA.png,
333      * _allciByRGBA.png, and _ci.bmp. PNG are prefered over BMP.
334      *
335      * For some reason there are texture packs that include them all. Some
336      * even have RGB textures named as _all.* and ARGB textures named as
337      * _rgb.*... Someone pleeeez write a GOOD guideline for the texture
338      * designers!!!
339      *
340      * We allow hires textures to have higher bpp than the N64 originals.
341      */
342     /* N64 formats
343      * Format: 0 - RGBA, 1 - YUV, 2 - CI, 3 - IA, 4 - I
344      * Size:   0 - 4bit, 1 - 8bit, 2 - 16bit, 3 - 32 bit
345      */
346
347     /*
348      * read in _rgb.* and _a.*
349      */
350     if (pfname == strstr(fname, "_rgb.") || pfname == strstr(fname, "_a.")) {
351       strcpy(pfname, "_rgb.png");
352       if (!boost::filesystem::exists(fname)) {
353         strcpy(pfname, "_rgb.bmp");
354         if (!boost::filesystem::exists(fname)) {
355 #if !DEBUG
356           INFO(80, L"-----\n");
357           INFO(80, L"path: %ls\n", dir_path.string().c_str());
358           INFO(80, L"file: %ls\n", it->path().leaf().c_str());
359 #endif
360           INFO(80, L"Error: missing _rgb.*! _a.* must be paired with _rgb.*!\n");
361           continue;
362         }
363       }
364       /* _a.png */
365       strcpy(pfname, "_a.png");
366       if ((fp = fopen(fname, "rb")) != NULL) {
367         tmptex = _txImage->readPNG(fp, &tmpwidth, &tmpheight, &tmpformat);
368         fclose(fp);
369       }
370       if (!tmptex) {
371         /* _a.bmp */
372         strcpy(pfname, "_a.bmp");
373         if ((fp = fopen(fname, "rb")) != NULL) {
374           tmptex = _txImage->readBMP(fp, &tmpwidth, &tmpheight, &tmpformat);
375           fclose(fp);
376         }
377       }
378       /* _rgb.png */
379       strcpy(pfname, "_rgb.png");
380       if ((fp = fopen(fname, "rb")) != NULL) {
381         tex = _txImage->readPNG(fp, &width, &height, &format);
382         fclose(fp);
383       }
384       if (!tex) {
385         /* _rgb.bmp */
386         strcpy(pfname, "_rgb.bmp");
387         if ((fp = fopen(fname, "rb")) != NULL) {
388           tex = _txImage->readBMP(fp, &width, &height, &format);
389           fclose(fp);
390         }
391       }
392       if (tmptex) {
393         /* check if _rgb.* and _a.* have matching size and format. */
394         if (!tex || width != tmpwidth || height != tmpheight ||
395             format != GR_TEXFMT_ARGB_8888 || tmpformat != GR_TEXFMT_ARGB_8888) {
396 #if !DEBUG
397           INFO(80, L"-----\n");
398           INFO(80, L"path: %ls\n", dir_path.string().c_str());
399           INFO(80, L"file: %ls\n", it->path().leaf().c_str());
400 #endif
401           if (!tex) {
402             INFO(80, L"Error: missing _rgb.*!\n");
403           } else if (width != tmpwidth || height != tmpheight) {
404             INFO(80, L"Error: _rgb.* and _a.* have mismatched width or height!\n");
405           } else if (format != GR_TEXFMT_ARGB_8888 || tmpformat != GR_TEXFMT_ARGB_8888) {
406             INFO(80, L"Error: _rgb.* or _a.* not in 32bit color!\n");
407           }
408           if (tex) free(tex);
409           if (tmptex) free(tmptex);
410           tex = NULL;
411           tmptex = NULL;
412           continue;
413         }
414       }
415       /* make adjustments */
416       if (tex) {
417         if (tmptex) {
418           /* merge (A)RGB and A comp */
419           DBG_INFO(80, L"merge (A)RGB and A comp\n");
420           int i;
421           for (i = 0; i < height * width; i++) {
422 #if 1
423             /* use R comp for alpha. this is what Rice uses. sigh... */
424             ((uint32*)tex)[i] &= 0x00ffffff;
425             ((uint32*)tex)[i] |= ((((uint32*)tmptex)[i] & 0x00ff0000) << 8);
426 #endif
427 #if 0
428             /* use libpng style grayscale conversion */
429             uint32 texel = ((uint32*)tmptex)[i];
430             uint32 acomp = (((texel >> 16) & 0xff) * 6969 +
431                             ((texel >>  8) & 0xff) * 23434 +
432                             ((texel      ) & 0xff) * 2365) / 32768;
433             ((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff);
434 #endif
435 #if 0
436             /* use the standard NTSC gray scale conversion */
437             uint32 texel = ((uint32*)tmptex)[i];
438             uint32 acomp = (((texel >> 16) & 0xff) * 299 +
439                             ((texel >>  8) & 0xff) * 587 +
440                             ((texel      ) & 0xff) * 114) / 1000;
441             ((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff);
442 #endif
443           }
444           free(tmptex);
445           tmptex = NULL;
446         } else {
447           /* clobber A comp. never a question of alpha. only RGB used. */
448 #if !DEBUG
449           INFO(80, L"-----\n");
450           INFO(80, L"path: %ls\n", dir_path.string().c_str());
451           INFO(80, L"file: %ls\n", it->path().leaf().c_str());
452 #endif
453           INFO(80, L"Warning: missing _a.*! only using _rgb.*. treat as opaque texture.\n");
454           int i;
455           for (i = 0; i < height * width; i++) {
456             ((uint32*)tex)[i] |= 0xff000000;
457           }
458         }
459       }
460     } else
461
462     /*
463      * read in _all.png, _all.dds, _allciByRGBA.png, _allciByRGBA.dds
464      * _ciByRGBA.png, _ciByRGBA.dds, _ci.bmp
465      */
466     if (pfname == strstr(fname, "_all.png") ||
467         pfname == strstr(fname, "_all.dds") ||
468 #ifdef WIN32
469         pfname == strstr(fname, "_allcibyrgba.png") ||
470         pfname == strstr(fname, "_allcibyrgba.dds") ||
471         pfname == strstr(fname, "_cibyrgba.png") ||
472         pfname == strstr(fname, "_cibyrgba.dds") ||
473 #else
474         pfname == strstr(fname, "_allciByRGBA.png") ||
475         pfname == strstr(fname, "_allciByRGBA.dds") ||
476         pfname == strstr(fname, "_ciByRGBA.png") ||
477         pfname == strstr(fname, "_ciByRGBA.dds") ||
478 #endif
479         pfname == strstr(fname, "_ci.bmp")) {
480       if ((fp = fopen(fname, "rb")) != NULL) {
481         if      (strstr(fname, ".png")) tex = _txImage->readPNG(fp, &width, &height, &format);
482         else if (strstr(fname, ".dds")) tex = _txImage->readDDS(fp, &width, &height, &format);
483         else                            tex = _txImage->readBMP(fp, &width, &height, &format);
484         fclose(fp);
485       }
486       /* XXX: auto-adjustment of dxt dds textures unsupported for now */
487       if (tex && strstr(fname, ".dds")) {
488         const float aspectratio = (width > height) ? (float)width/(float)height : (float)height/(float)width;
489         if (!(aspectratio == 1.0 ||
490               aspectratio == 2.0 ||
491               aspectratio == 4.0 ||
492               aspectratio == 8.0)) {
493           free(tex);
494           tex = NULL;
495 #if !DEBUG
496           INFO(80, L"-----\n");
497           INFO(80, L"path: %ls\n", dir_path.string().c_str());
498           INFO(80, L"file: %ls\n", it->path().leaf().c_str());
499 #endif
500           INFO(80, L"Error: W:H aspect ratio range not 8:1 - 1:8!\n");
501           continue;
502         }
503         if (width  != _txReSample->nextPow2(width) ||
504             height != _txReSample->nextPow2(height)) {
505           free(tex);
506           tex = NULL;
507 #if !DEBUG
508           INFO(80, L"-----\n");
509           INFO(80, L"path: %ls\n", dir_path.string().c_str());
510           INFO(80, L"file: %ls\n", it->path().leaf().c_str());
511 #endif
512           INFO(80, L"Error: not power of 2 size!\n");
513           continue;
514         }
515       }
516     }
517
518     /* if we do not have a texture at this point we are screwed */
519     if (!tex) {
520 #if !DEBUG
521       INFO(80, L"-----\n");
522       INFO(80, L"path: %ls\n", dir_path.string().c_str());
523       INFO(80, L"file: %ls\n", it->path().leaf().c_str());
524 #endif
525       INFO(80, L"Error: load failed!\n");
526       continue;
527     }
528     DBG_INFO(80, L"read in as %d x %d gfmt:%x\n", tmpwidth, tmpheight, tmpformat);
529
530     /* check if size and format are OK */
531     if (!(format == GR_TEXFMT_ARGB_8888     ||
532           format == GR_TEXFMT_P_8           ||
533           format == GR_TEXFMT_ARGB_CMP_DXT1 ||
534           format == GR_TEXFMT_ARGB_CMP_DXT3 ||
535           format == GR_TEXFMT_ARGB_CMP_DXT5) ||
536         (width * height) < 4) { /* TxQuantize requirement: width * height must be 4 or larger. */
537       free(tex);
538       tex = NULL;
539 #if !DEBUG
540       INFO(80, L"-----\n");
541       INFO(80, L"path: %ls\n", dir_path.string().c_str());
542       INFO(80, L"file: %ls\n", it->path().leaf().c_str());
543 #endif
544       INFO(80, L"Error: not width * height > 4 or 8bit palette color or 32bpp or dxt1 or dxt3 or dxt5!\n");
545       continue;
546     }
547
548     /* analyze and determine best format to quantize */
549     if (format == GR_TEXFMT_ARGB_8888) {
550       int i;
551       int alphabits = 0;
552       int fullalpha = 0;
553       boolean intensity = 1;
554
555       if (!(_options & LET_TEXARTISTS_FLY)) {
556         /* HACK ALERT! */
557         /* Account for Rice's weirdness with fmt:0 siz:2 textures.
558          * Although the conditions are relaxed with other formats,
559          * the D3D RGBA5551 surface is used for this format in certain
560          * cases. See Nintemod's SuperMario64 life gauge and power
561          * meter. The same goes for fmt:2 textures. See Mollymutt's
562          * PaperMario text. */
563         if ((fmt == 0 && siz == 2) || fmt == 2) {
564           DBG_INFO(80, L"Remove black, white, etc borders along the alpha edges.\n");
565           /* round A comp */
566           for (i = 0; i < height * width; i++) {
567             uint32 texel = ((uint32*)tex)[i];
568             ((uint32*)tex)[i] = ((texel & 0xff000000) == 0xff000000 ? 0xff000000 : 0) |
569                                 (texel & 0x00ffffff);
570           }
571           /* Substitute texel color with the average of the surrounding
572            * opaque texels. This removes borders regardless of hardware
573            * texture filtering (bilinear, etc). */
574           int j;
575           for (i = 0; i < height; i++) {
576             for (j = 0; j < width; j++) {
577               uint32 texel = ((uint32*)tex)[i * width + j];
578               if ((texel & 0xff000000) != 0xff000000) {
579                 uint32 tmptexel[8];
580                 uint32 k, numtexel, r, g, b;
581                 numtexel = r = g = b = 0;
582                 memset(&tmptexel, 0, sizeof(tmptexel));
583                 if (i > 0) {
584                   tmptexel[0] = ((uint32*)tex)[(i - 1) * width + j];                        /* north */
585                   if (j > 0)         tmptexel[1] = ((uint32*)tex)[(i - 1) * width + j - 1]; /* north-west */
586                   if (j < width - 1) tmptexel[2] = ((uint32*)tex)[(i - 1) * width + j + 1]; /* north-east */
587                 }
588                 if (i < height - 1) {
589                   tmptexel[3] = ((uint32*)tex)[(i + 1) * width + j];                        /* south */
590                   if (j > 0)         tmptexel[4] = ((uint32*)tex)[(i + 1) * width + j - 1]; /* south-west */
591                   if (j < width - 1) tmptexel[5] = ((uint32*)tex)[(i + 1) * width + j + 1]; /* south-east */
592                 }
593                 if (j > 0)         tmptexel[6] = ((uint32*)tex)[i * width + j - 1]; /* west */
594                 if (j < width - 1) tmptexel[7] = ((uint32*)tex)[i * width + j + 1]; /* east */
595                 for (k = 0; k < 8; k++) {
596                   if ((tmptexel[k] & 0xff000000) == 0xff000000) {
597                     r += ((tmptexel[k] & 0x00ff0000) >> 16);
598                     g += ((tmptexel[k] & 0x0000ff00) >>  8);
599                     b += ((tmptexel[k] & 0x000000ff)      );
600                     numtexel++;
601                   }
602                 }
603                 if (numtexel) {
604                   ((uint32*)tex)[i * width + j] = ((r / numtexel) << 16) |
605                                                   ((g / numtexel) <<  8) |
606                                                   ((b / numtexel)      );
607                 } else {
608                   ((uint32*)tex)[i * width + j] = texel & 0x00ffffff;
609                 }
610               }
611             }
612           }
613         }
614       }
615
616       /* simple analysis of texture */
617       for (i = 0; i < height * width; i++) {
618         uint32 texel = ((uint32*)tex)[i];
619         if (alphabits != 8) {
620 #if AGGRESSIVE_QUANTIZATION
621           if ((texel & 0xff000000) < 0x00000003) {
622             alphabits = 1;
623             fullalpha++;
624           } else if ((texel & 0xff000000) < 0xfe000000) {
625             alphabits = 8;
626           }
627 #else
628           if ((texel & 0xff000000) == 0x00000000) {
629             alphabits = 1;
630             fullalpha++;
631           } else if ((texel & 0xff000000) != 0xff000000) {
632             alphabits = 8;
633           }
634 #endif
635         }
636         if (intensity) {
637           int rcomp = (texel >> 16) & 0xff;
638           int gcomp = (texel >>  8) & 0xff;
639           int bcomp = (texel      ) & 0xff;
640 #if AGGRESSIVE_QUANTIZATION
641           if (abs(rcomp - gcomp) > 8 || abs(rcomp - bcomp) > 8 || abs(gcomp - bcomp) > 8) intensity = 0;
642 #else
643           if (rcomp != gcomp || rcomp != bcomp || gcomp != bcomp) intensity = 0;
644 #endif
645         }
646         if (!intensity && alphabits == 8) break;
647       }
648       DBG_INFO(80, L"required alpha bits:%d zero acomp texels:%d rgb as intensity:%d\n", alphabits, fullalpha, intensity);
649
650       /* preparations based on above analysis */
651 #if !REDUCE_TEXTURE_FOOTPRINT
652       if (_maxbpp < 32 || _options & (FORCE16BPP_HIRESTEX|COMPRESSION_MASK)) {
653 #endif
654         if      (alphabits == 0) destformat = GR_TEXFMT_RGB_565;
655         else if (alphabits == 1) destformat = GR_TEXFMT_ARGB_1555;
656         else                     destformat = GR_TEXFMT_ARGB_8888;
657 #if !REDUCE_TEXTURE_FOOTPRINT
658       } else {
659         destformat = GR_TEXFMT_ARGB_8888;
660       }
661 #endif
662       if (fmt == 4 && alphabits == 0) {
663         destformat = GR_TEXFMT_ARGB_8888;
664         /* Rice I format; I = (R + G + B) / 3 */
665         for (i = 0; i < height * width; i++) {
666           uint32 texel = ((uint32*)tex)[i];
667           uint32 icomp = (((texel >> 16) & 0xff) +
668                           ((texel >>  8) & 0xff) +
669                           ((texel      ) & 0xff)) / 3;
670           ((uint32*)tex)[i] = (icomp << 24) | (texel & 0x00ffffff);
671         }
672       }
673       if (intensity) {
674         if (alphabits == 0) {
675           if (fmt == 4) destformat = GR_TEXFMT_ALPHA_8;
676           else          destformat = GR_TEXFMT_INTENSITY_8;
677         } else {
678           destformat = GR_TEXFMT_ALPHA_INTENSITY_88;
679         }
680       }
681
682       DBG_INFO(80, L"best gfmt:%x\n", destformat);
683     }
684     /*
685      * Rice hi-res textures: end */
686
687
688     /* XXX: only ARGB8888 for now. comeback to this later... */
689     if (format == GR_TEXFMT_ARGB_8888) {
690
691 #if TEXTURE_TILING
692
693       /* Glide64 style texture tiling */
694       /* NOTE: narrow wide textures can be tiled into 256x256 size textures */
695
696       /* adjust texture size to allow tiling for V1, Rush, V2, Banshee, V3 */
697       /* NOTE: we skip this for palette textures that need minification
698        * becasue it will look ugly. */
699
700       /* minification */
701       {
702         int ratio = 1;
703
704         /* minification to enable glide64 style texture tiling */
705         /* determine the minification ratio to tile the texture into 256x256 size */
706         if ((_options & TILE_HIRESTEX) && _maxwidth >= 256 && _maxheight >= 256) {
707           DBG_INFO(80, L"determine minification ratio to tile\n");
708           tmpwidth = width;
709           tmpheight = height;
710           if (height > 256) {
711             ratio = ((height - 1) >> 8) + 1;
712             tmpwidth = width / ratio;
713             tmpheight = height / ratio;
714             DBG_INFO(80, L"height > 256, minification ratio:%d %d x %d -> %d x %d\n",
715                      ratio, width, height, tmpwidth, tmpheight);
716           }
717           if (tmpwidth > 256 && (((tmpwidth - 1) >> 8) + 1) * tmpheight > 256) {
718             ratio *= ((((((tmpwidth - 1) >> 8) + 1) * tmpheight) - 1) >> 8) + 1;
719             DBG_INFO(80, L"width > 256, minification ratio:%d %d x %d -> %d x %d\n",
720                      ratio, width, height, width / ratio, height / ratio);
721           }
722         } else {
723           /* normal minification to fit max texture size */
724           if (width > _maxwidth || height > _maxheight) {
725             DBG_INFO(80, L"determine minification ratio to fit max texture size\n");
726             tmpwidth = width;
727             tmpheight = height;
728             while (tmpwidth > _maxwidth) {
729               tmpheight >>= 1;
730               tmpwidth >>= 1;
731               ratio <<= 1;
732             }
733             while (tmpheight > _maxheight) {
734               tmpheight >>= 1;
735               tmpwidth >>= 1;
736               ratio <<= 1;
737             }
738             DBG_INFO(80, L"minification ratio:%d %d x %d -> %d x %d\n",
739                      ratio, width, height, tmpwidth, tmpheight);
740           }
741         }
742
743         if (ratio > 1) {
744           if (!_txReSample->minify(&tex, &width, &height, ratio)) {
745             free(tex);
746             tex = NULL;
747             DBG_INFO(80, L"Error: minification failed!\n");
748             continue;
749           }
750         }
751       }
752
753       /* tiling */
754       if ((_options & TILE_HIRESTEX) && _maxwidth >= 256 && _maxheight >= 256) {
755         boolean usetile = 0;
756
757         /* to tile or not to tile, that is the question */
758         if (width > 256 && height <= 128 && (((width - 1) >> 8) + 1) * height <= 256) {
759
760           if (width > _maxwidth) usetile = 1;
761           else {
762             /* tile if the tiled texture memory footprint is smaller */
763             int tilewidth  = 256;
764             int tileheight = _txReSample->nextPow2((((width - 1) >> 8) + 1) * height);
765             tmpwidth  = width;
766             tmpheight = height;
767
768             /* 3dfx Glide3 tmpheight, W:H aspect ratio range (8:1 - 1:8) */
769             if (tilewidth > (tileheight << 3)) tileheight = tilewidth >> 3;
770
771             /* HACKALERT: see TxReSample::pow2(); */
772             if      (tmpwidth  > 64) tmpwidth  -= 4;
773             else if (tmpwidth  > 16) tmpwidth  -= 2;
774             else if (tmpwidth  >  4) tmpwidth  -= 1;
775
776             if      (tmpheight > 64) tmpheight -= 4;
777             else if (tmpheight > 16) tmpheight -= 2;
778             else if (tmpheight >  4) tmpheight -= 1;
779
780             tmpwidth  = _txReSample->nextPow2(tmpwidth);
781             tmpheight = _txReSample->nextPow2(tmpheight);
782
783             /* 3dfx Glide3 tmpheight, W:H aspect ratio range (8:1 - 1:8) */
784             if (tmpwidth > tmpheight) {
785               if (tmpwidth  > (tmpheight << 3)) tmpheight = tmpwidth  >> 3;
786             } else {
787               if (tmpheight > (tmpwidth  << 3)) tmpwidth  = tmpheight >> 3;
788             }
789
790             usetile = (tilewidth * tileheight < tmpwidth * tmpheight);
791           }
792
793         }
794
795         /* tile it! do the actual tiling into 256x256 size */
796         if (usetile) {
797           DBG_INFO(80, L"Glide64 style texture tiling\n");
798
799           int x, y, z, ratio, offset;
800           offset = 0;
801           ratio = ((width - 1) >> 8) + 1;
802           tmptex = (uint8 *)malloc(_txUtil->sizeofTx(256, height * ratio, format));
803           if (tmptex) {
804             for (x = 0; x < ratio; x++) {
805               for (y = 0; y < height; y++) {
806                 if (x < ratio - 1) {
807                   memcpy(&tmptex[offset << 2], &tex[(x * 256 + y * width) << 2], 256 << 2);
808                 } else {
809                   for (z = 0; z < width - 256 * (ratio - 1); z++) {
810                     ((uint32*)tmptex)[offset + z] = ((uint32*)tex)[x * 256 + y * width + z];
811                   }
812                   for (; z < 256; z++) {
813                     ((uint32*)tmptex)[offset + z] = ((uint32*)tmptex)[offset + z - 1];
814                   }
815                 }
816                 offset += 256;
817               }
818             }
819             free(tex);
820             tex = tmptex;
821             untiled_width = width;
822             untiled_height = height;
823             width = 256;
824             height *= ratio;
825             DBG_INFO(80, L"Tiled: %d x %d -> %d x %d\n", untiled_width, untiled_height, width, height);
826           }
827         }
828       }
829
830 #else  /* TEXTURE_TILING */
831
832       /* minification */
833       if (width > _maxwidth || height > _maxheight) {
834         int ratio = 1;
835         if (width / _maxwidth > height / _maxheight) {
836           ratio = (int)ceil((double)width / _maxwidth);
837         } else {
838           ratio = (int)ceil((double)height / _maxheight);
839         }
840         if (!_txReSample->minify(&tex, &width, &height, ratio)) {
841           free(tex);
842           tex = NULL;
843           DBG_INFO(80, L"Error: minification failed!\n");
844           continue;
845         }
846       }
847
848 #endif /* TEXTURE_TILING */
849
850       /* texture compression */
851       if ((_options & COMPRESSION_MASK) &&
852           (width >= 64 && height >= 64) /* Texture compression is not suitable for low pixel coarse detail
853                                          * textures. The assumption here is that textures larger than 64x64
854                                          * have enough detail to produce decent quality when compressed. The
855                                          * down side is that narrow stripped textures that the N64 often use
856                                          * for large background textures are also ignored. It would be more
857                                          * reasonable if decisions are made based on fourier-transform
858                                          * spectrum or RMS error.
859                                          *
860                                          * NOTE: texture size must be checked before expanding to pow2 size.
861                                          */
862           ) {
863         int dataSize = 0;
864         int compressionType = _options & COMPRESSION_MASK;
865
866 #if POW2_TEXTURES
867 #if (POW2_TEXTURES == 2)
868         /* 3dfx Glide3x aspect ratio (8:1 - 1:8) */
869         if (!_txReSample->nextPow2(&tex, &width , &height, 32, 1)) {
870 #else
871         /* normal pow2 expansion */
872         if (!_txReSample->nextPow2(&tex, &width , &height, 32, 0)) {
873 #endif
874           free(tex);
875           tex = NULL;
876           DBG_INFO(80, L"Error: aspect ratio adjustment failed!\n");
877           continue;
878         }
879 #endif
880
881         switch (_options & COMPRESSION_MASK) {
882         case S3TC_COMPRESSION:
883           switch (destformat) {
884           case GR_TEXFMT_ARGB_8888:
885 #if GLIDE64_DXTN
886           case GR_TEXFMT_ARGB_1555: /* for ARGB1555 use DXT5 instead of DXT1 */
887 #endif
888           case GR_TEXFMT_ALPHA_INTENSITY_88:
889             dataSize = width * height;
890             break;
891 #if !GLIDE64_DXTN
892           case GR_TEXFMT_ARGB_1555:
893 #endif
894           case GR_TEXFMT_RGB_565:
895           case GR_TEXFMT_INTENSITY_8:
896             dataSize = (width * height) >> 1;
897             break;
898           case GR_TEXFMT_ALPHA_8: /* no size benefit with dxtn */
899             ;
900           }
901           break;
902         case FXT1_COMPRESSION:
903           switch (destformat) {
904           case GR_TEXFMT_ARGB_1555:
905           case GR_TEXFMT_RGB_565:
906           case GR_TEXFMT_INTENSITY_8:
907             dataSize = (width * height) >> 1;
908             break;
909             /* XXX: textures that use 8bit alpha channel look bad with the current
910              * fxt1 library, so we substitute it with dxtn for now. afaik all gfx
911              * cards that support fxt1 also support dxtn. (3dfx and Intel) */
912           case GR_TEXFMT_ALPHA_INTENSITY_88:
913           case GR_TEXFMT_ARGB_8888:
914             compressionType = S3TC_COMPRESSION;
915             dataSize = width * height;
916             break;
917           case GR_TEXFMT_ALPHA_8: /* no size benefit with dxtn */
918             ;
919           }
920         }
921         /* compress it! */
922         if (dataSize) {
923 #if 0 /* TEST: dither before compression for better results with gradients */
924           tmptex = (uint8 *)malloc(_txUtil->sizeofTx(width, height, destformat));
925           if (tmptex) {
926             if (_txQuantize->quantize(tex, tmptex, width, height, GR_TEXFMT_ARGB_8888, destformat, 0))
927               _txQuantize->quantize(tmptex, tex, width, height, destformat, GR_TEXFMT_ARGB_8888, 0);
928             free(tmptex);
929           }
930 #endif
931           tmptex = (uint8 *)malloc(dataSize);
932           if (tmptex) {
933             if (_txQuantize->compress(tex, tmptex,
934                                       width, height, destformat,
935                                       &tmpwidth, &tmpheight, &tmpformat,
936                                       compressionType)) {
937               free(tex);
938               tex = tmptex;
939               width = tmpwidth;
940               height = tmpheight;
941               format = destformat = tmpformat;
942             } else {
943               free(tmptex);
944             }
945           }
946         }
947
948       } else {
949
950 #if POW2_TEXTURES
951 #if (POW2_TEXTURES == 2)
952         /* 3dfx Glide3x aspect ratio (8:1 - 1:8) */
953         if (!_txReSample->nextPow2(&tex, &width , &height, 32, 1)) {
954 #else
955         /* normal pow2 expansion */
956         if (!_txReSample->nextPow2(&tex, &width , &height, 32, 0)) {
957 #endif
958           free(tex);
959           tex = NULL;
960           DBG_INFO(80, L"Error: aspect ratio adjustment failed!\n");
961           continue;
962         }
963 #endif
964       }
965
966       /* quantize */
967       {
968         tmptex = (uint8 *)malloc(_txUtil->sizeofTx(width, height, destformat));
969         if (tmptex) {
970           switch (destformat) {
971           case GR_TEXFMT_ARGB_8888:
972           case GR_TEXFMT_ARGB_4444:
973 #if !REDUCE_TEXTURE_FOOTPRINT
974             if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)
975 #endif
976               destformat = GR_TEXFMT_ARGB_4444;
977             break;
978           case GR_TEXFMT_ARGB_1555:
979 #if !REDUCE_TEXTURE_FOOTPRINT
980             if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)
981 #endif
982               destformat = GR_TEXFMT_ARGB_1555;
983             break;
984           case GR_TEXFMT_RGB_565:
985 #if !REDUCE_TEXTURE_FOOTPRINT
986             if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)
987 #endif
988               destformat = GR_TEXFMT_RGB_565;
989             break;
990           case GR_TEXFMT_ALPHA_INTENSITY_88:
991           case GR_TEXFMT_ALPHA_INTENSITY_44:
992 #if !REDUCE_TEXTURE_FOOTPRINT
993             destformat = GR_TEXFMT_ALPHA_INTENSITY_88;
994 #else
995             destformat = GR_TEXFMT_ALPHA_INTENSITY_44;
996 #endif
997             break;
998           case GR_TEXFMT_ALPHA_8:
999             destformat = GR_TEXFMT_ALPHA_8; /* yes, this is correct. ALPHA_8 instead of INTENSITY_8 */
1000             break;
1001           case GR_TEXFMT_INTENSITY_8:
1002             destformat = GR_TEXFMT_INTENSITY_8;
1003           }
1004           if (_txQuantize->quantize(tex, tmptex, width, height, GR_TEXFMT_ARGB_8888, destformat, 0)) {
1005             format = destformat;
1006             free(tex);
1007             tex = tmptex;
1008           }
1009         }
1010       }
1011
1012     }
1013
1014
1015     /* last minute validations */
1016     if (!tex || !chksum || !width || !height || !format || width > _maxwidth || height > _maxheight) {
1017 #if !DEBUG
1018       INFO(80, L"-----\n");
1019       INFO(80, L"path: %ls\n", dir_path.string().c_str());
1020       INFO(80, L"file: %ls\n", it->path().leaf().c_str());
1021 #endif
1022       if (tex) {
1023         free(tex);
1024         tex = NULL;
1025         INFO(80, L"Error: bad format or size! %d x %d gfmt:%x\n", width, height, format);
1026       } else {
1027         INFO(80, L"Error: load failed!!\n");
1028       }
1029       continue;
1030     }
1031
1032     /* load it into hires texture cache. */
1033     {
1034       uint64 chksum64 = (uint64)palchksum;
1035       chksum64 <<= 32;
1036       chksum64 |= (uint64)chksum;
1037
1038       GHQTexInfo tmpInfo;
1039       memset(&tmpInfo, 0, sizeof(GHQTexInfo));
1040
1041       tmpInfo.data = tex;
1042       tmpInfo.width = width;
1043       tmpInfo.height = height;
1044       tmpInfo.format = format;
1045       tmpInfo.largeLodLog2 = _txUtil->grLodLog2(width, height);
1046       tmpInfo.smallLodLog2 = tmpInfo.largeLodLog2;
1047       tmpInfo.aspectRatioLog2 = _txUtil->grAspectRatioLog2(width, height);
1048       tmpInfo.is_hires_tex = 1;
1049
1050 #if TEXTURE_TILING
1051       /* Glide64 style texture tiling. */
1052       if (untiled_width && untiled_height) {
1053         tmpInfo.tiles = ((untiled_width - 1) >> 8) + 1;
1054         tmpInfo.untiled_width = untiled_width;
1055         tmpInfo.untiled_height = untiled_height;
1056       }
1057 #endif
1058
1059       /* remove redundant in cache */
1060       if (replace && TxCache::del(chksum64)) {
1061         DBG_INFO(80, L"removed duplicate old cache.\n");
1062       }
1063
1064       /* add to cache */
1065       if (TxCache::add(chksum64, &tmpInfo)) {
1066         now = SDL_GetTicks();
1067         diff = now - last;
1068
1069         /* Callback to display hires texture info.
1070          * Gonetz <gonetz(at)ngs.ru> */
1071         if (_callback && diff > 250) {
1072           wchar_t tmpbuf[MAX_PATH];
1073           mbstowcs(tmpbuf, fname, MAX_PATH);
1074           (*_callback)(L"[%d] total mem:%.2fmb - %ls\n", _cache.size(), (float)_totalSize/1000000, tmpbuf);
1075           last = now;
1076         }
1077         DBG_INFO(80, L"texture loaded!\n");
1078       }
1079       free(tex);
1080     }
1081
1082   }
1083
1084   if (CHDIR(curpath) != 0)
1085       ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);
1086
1087   return 1;
1088 }