diff --git a/projects/msvc/libGLideNHQ.vcxproj b/projects/msvc/libGLideNHQ.vcxproj index 2166cb09..7ca533c7 100644 --- a/projects/msvc/libGLideNHQ.vcxproj +++ b/projects/msvc/libGLideNHQ.vcxproj @@ -126,7 +126,9 @@ + + diff --git a/projects/msvc/libGLideNHQ.vcxproj.filters b/projects/msvc/libGLideNHQ.vcxproj.filters index c8484e32..e98cb16a 100644 --- a/projects/msvc/libGLideNHQ.vcxproj.filters +++ b/projects/msvc/libGLideNHQ.vcxproj.filters @@ -22,9 +22,15 @@ Source Files + + Source Files + Source Files + + Source Files + Source Files diff --git a/src/Config.cpp b/src/Config.cpp index c6f72295..bdc68473 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -102,6 +102,7 @@ void Config::resetToDefaults() textureFilter.txEnhancedTextureFileStorage = 0; textureFilter.txHiresTextureFileStorage = 0; + textureFilter.txNoTextureFileStorage = 0; api().GetUserDataPath(textureFilter.txPath); gln_wcscat(textureFilter.txPath, wst("/hires_texture")); diff --git a/src/Config.h b/src/Config.h index ddb762fd..4b0b5e05 100644 --- a/src/Config.h +++ b/src/Config.h @@ -175,6 +175,7 @@ struct Config u32 txEnhancedTextureFileStorage; // Use file storage instead of memory cache for enhanced textures. u32 txHiresTextureFileStorage; // Use file storage instead of memory cache for hires textures. + u32 txNoTextureFileStorage; // Use no file storage or cache for hires textures. wchar_t txPath[PLUGIN_PATH_SIZE]; // Path to texture packs wchar_t txCachePath[PLUGIN_PATH_SIZE]; // Path to store texture cache, that is .htc files diff --git a/src/GLideNHQ/CMakeLists.txt b/src/GLideNHQ/CMakeLists.txt index 6f8562bf..1e98896d 100644 --- a/src/GLideNHQ/CMakeLists.txt +++ b/src/GLideNHQ/CMakeLists.txt @@ -12,7 +12,9 @@ set(GLideNHQ_SOURCES TxDbg.cpp TxFilter.cpp TxFilterExport.cpp + TxHiResLoader.cpp TxHiResCache.cpp + TxHiResNoCache.cpp TxImage.cpp TxQuantize.cpp TxReSample.cpp diff --git a/src/GLideNHQ/Ext_TxFilter.h b/src/GLideNHQ/Ext_TxFilter.h index 7185637d..439ae9b7 100644 --- a/src/GLideNHQ/Ext_TxFilter.h +++ b/src/GLideNHQ/Ext_TxFilter.h @@ -93,6 +93,7 @@ typedef unsigned char boolean; #define JABO_HIRESTEXTURES 0x00030000 #define FILE_CACHE_MASK 0x00300000 +#define FILE_NOTEXCACHE 0x08500000 #define FILE_TEXCACHE 0x00100000 #define FILE_HIRESTEXCACHE 0x00200000 #define GZ_TEXCACHE 0x00400000 diff --git a/src/GLideNHQ/TxFilter.cpp b/src/GLideNHQ/TxFilter.cpp index 6ef69086..e266d1e9 100644 --- a/src/GLideNHQ/TxFilter.cpp +++ b/src/GLideNHQ/TxFilter.cpp @@ -38,8 +38,8 @@ void TxFilter::clear() { - /* clear hires texture cache */ - delete _txHiResCache; + /* clear hires texture loader */ + delete _txHiResLoader; /* clear texture cache */ delete _txTexCache; @@ -71,7 +71,7 @@ TxFilter::TxFilter(int maxwidth, , _tex2(nullptr) , _txQuantize(nullptr) , _txTexCache(nullptr) - , _txHiResCache(nullptr) + , _txHiResLoader(nullptr) , _txImage(nullptr) { /* HACKALERT: the emulator misbehaves and sometimes forgets to shutdown */ @@ -150,9 +150,17 @@ TxFilter::TxFilter(int maxwidth, /* hires texture */ #if HIRES_TEXTURE - _txHiResCache = new TxHiResCache(_maxwidth, _maxheight, _maxbpp, _options, texCachePath, texPackPath, _ident.c_str(), callback); + if ((_options & FILE_NOTEXCACHE) == FILE_NOTEXCACHE) { + wchar_t fullTexPackPath[MAX_PATH]; + wcscpy(fullTexPackPath, texPackPath); + wcscat(fullTexPackPath, OSAL_DIR_SEPARATOR_STR); + wcscat(fullTexPackPath, ident); + _txHiResLoader = new TxHiResNoCache(_maxwidth, _maxheight, _maxbpp, _options, texCachePath, texPackPath, fullTexPackPath, _ident.c_str(), callback); + } else { + _txHiResLoader = new TxHiResCache(_maxwidth, _maxheight, _maxbpp, _options, texCachePath, texPackPath, _ident.c_str(), callback); + } - if (_txHiResCache->empty()) + if (_txHiResLoader->empty()) _options &= ~HIRESTEXTURES_MASK; #endif @@ -479,7 +487,7 @@ TxFilter::hirestex(uint64 g64crc, uint64 r_crc64, uint16 *palette, GHQTexInfo *i #if HIRES_TEXTURE /* check if we have it in hires memory cache. */ if ((_options & HIRESTEXTURES_MASK) && r_crc64) { - if (_txHiResCache->get(r_crc64, info)) { + if (_txHiResLoader->get(r_crc64, info)) { DBG_INFO(80, wst("hires hit: %d x %d gfmt:%x\n"), info->width, info->height, info->format); /* TODO: Enable emulation for special N64 combiner modes. There are few ways @@ -506,8 +514,8 @@ TxFilter::hirestex(uint64 g64crc, uint64 r_crc64, uint16 *palette, GHQTexInfo *i return 1; /* yep, got it */ } - if (_txHiResCache->get((r_crc64 >> 32), info) || - _txHiResCache->get((r_crc64 & 0xffffffff), info)) { + if (_txHiResLoader->get((r_crc64 >> 32), info) || + _txHiResLoader->get((r_crc64 & 0xffffffff), info)) { DBG_INFO(80, wst("hires hit: %d x %d gfmt:%x\n"), info->width, info->height, info->format); /* for true CI textures, we use the passed in palette to convert to @@ -543,7 +551,7 @@ TxFilter::hirestex(uint64 g64crc, uint64 r_crc64, uint16 *palette, GHQTexInfo *i setTextureFormat(format, info); /* XXX: add to hires texture cache!!! */ - _txHiResCache->add(r_crc64, info); + _txHiResLoader->add(r_crc64, info); DBG_INFO(80, wst("COLOR_INDEX8 loaded as gfmt:%x!\n"), u32(format)); } @@ -639,7 +647,7 @@ TxFilter::reloadhirestex() { DBG_INFO(80, wst("Reload hires textures from texture pack.\n")); - if (_txHiResCache->load(0) && !_txHiResCache->empty()) { + if (_txHiResLoader->reload()) { _options |= HIRESTEXTURES_MASK; return 1; } @@ -655,6 +663,6 @@ TxFilter::dumpcache() /* hires texture */ #if HIRES_TEXTURE - _txHiResCache->dump(); + _txHiResLoader->dump(); #endif } diff --git a/src/GLideNHQ/TxFilter.h b/src/GLideNHQ/TxFilter.h index f3e24bf1..a331c697 100644 --- a/src/GLideNHQ/TxFilter.h +++ b/src/GLideNHQ/TxFilter.h @@ -27,6 +27,8 @@ #include "TxInternal.h" #include "TxQuantize.h" #include "TxHiResCache.h" +#include "TxHiResNoCache.h" +#include "TxHiResLoader.h" #include "TxTexCache.h" #include "TxUtil.h" #include "TxImage.h" @@ -47,7 +49,7 @@ private: tx_wstring _dumpPath; TxQuantize *_txQuantize; TxTexCache *_txTexCache; - TxHiResCache *_txHiResCache; + TxHiResLoader *_txHiResLoader; TxImage *_txImage; boolean _initialized; void clear(); diff --git a/src/GLideNHQ/TxHiResCache.cpp b/src/GLideNHQ/TxHiResCache.cpp index 47022dd9..9669b099 100644 --- a/src/GLideNHQ/TxHiResCache.cpp +++ b/src/GLideNHQ/TxHiResCache.cpp @@ -28,17 +28,10 @@ #pragma warning(disable: 4786) #endif -/* use power of 2 texture size - * (0:disable, 1:enable, 2:3dfx) */ -#define POW2_TEXTURES 0 - -/* use aggressive format assumption for quantization - * (0:disable, 1:enable, 2:extreme) */ -#define AGGRESSIVE_QUANTIZATION 1 - #include "TxHiResCache.h" #include "TxDbg.h" #include +#include #include #include #include @@ -59,16 +52,10 @@ TxHiResCache::TxHiResCache(int maxwidth, const wchar_t *ident, dispInfoFuncExt callback) : TxCache((options & ~(GZ_TEXCACHE | FILE_TEXCACHE)), 0, cachePath, ident, callback) - , _maxwidth(maxwidth) - , _maxheight(maxheight) - , _maxbpp(maxbpp) + , TxHiResLoader(maxwidth, maxheight, maxbpp, options) , _abortLoad(false) , _cacheDumped(false) - , _txImage(new TxImage()) - , _txQuantize(new TxQuantize()) - , _txReSample(new TxReSample()) { - if (texPackPath) _texPackPath.assign(texPackPath); @@ -125,7 +112,7 @@ boolean TxHiResCache::_HiResTexPackPathExists() const return osal_path_existsW(dir_path.c_str()); } -bool TxHiResCache::load(boolean replace) /* 0 : reload, 1 : replace partial */ +bool TxHiResCache::_load(boolean replace) /* 0 : reload, 1 : replace partial */ { if (_texPackPath.empty() || _ident.empty()) return false; @@ -151,7 +138,7 @@ bool TxHiResCache::load(boolean replace) /* 0 : reload, 1 : replace partial */ dir_path += OSAL_DIR_SEPARATOR_STR; dir_path += _ident; - const LoadResult res = loadHiResTextures(dir_path.c_str(), replace); + const LoadResult res = _loadHiResTextures(dir_path.c_str(), replace); if (res == resError) { if (_callback) (*_callback)(wst("Texture pack load failed. Clear hiresolution texture cache.\n")); INFO(80, wst("Texture pack load failed. Clear hiresolution texture cache.\n")); @@ -162,7 +149,12 @@ bool TxHiResCache::load(boolean replace) /* 0 : reload, 1 : replace partial */ return false; } -TxHiResCache::LoadResult TxHiResCache::loadHiResTextures(const wchar_t * dir_path, boolean replace) +bool TxHiResCache::reload() +{ + return _load(0) && !TxCache::empty(); +} + +TxHiResCache::LoadResult TxHiResCache::_loadHiResTextures(const wchar_t * dir_path, boolean replace) { DBG_INFO(80, wst("-----\n")); DBG_INFO(80, wst("path: %ls\n"), dir_path); @@ -193,8 +185,8 @@ TxHiResCache::LoadResult TxHiResCache::loadHiResTextures(const wchar_t * dir_pat tx_wstring texturefilename; do { - - if (KBHIT(0x1B)) { + osal_keys_update_state(); + if (osal_is_key_pressed(KEY_Escape, 0x0001)) { _abortLoad = true; if (_callback) (*_callback)(wst("Aborted loading hiresolution texture!\n")); INFO(80, wst("Error: aborted loading hiresolution texture!\n")); @@ -210,13 +202,14 @@ TxHiResCache::LoadResult TxHiResCache::loadHiResTextures(const wchar_t * dir_pat if (wccmp(foundfilename, wst("."))) // These files we don't need continue; + texturefilename.assign(dir_path); texturefilename += OSAL_DIR_SEPARATOR_STR; texturefilename += foundfilename; /* recursive read into sub-directory */ if (osal_is_directory(texturefilename.c_str())) { - result = loadHiResTextures(texturefilename.c_str(), replace); + result = _loadHiResTextures(texturefilename.c_str(), replace); if (result == resOk) continue; else @@ -229,103 +222,25 @@ TxHiResCache::LoadResult TxHiResCache::loadHiResTextures(const wchar_t * dir_pat int width = 0, height = 0; ColorFormat format = graphics::internalcolorFormat::NOCOLOR; uint8 *tex = nullptr; - int tmpwidth = 0, tmpheight = 0; - ColorFormat tmpformat = graphics::internalcolorFormat::NOCOLOR; - uint8 *tmptex = nullptr; - ColorFormat destformat = graphics::internalcolorFormat::NOCOLOR; /* Rice hi-res textures: begin */ - uint32 chksum = 0, fmt = 0, siz = 0, palchksum = 0; - bool hasWildcard = false; - char *pfname = nullptr, fname[MAX_PATH]; - std::string ident; + uint32 chksum = 0, fmt = 0, siz = 0, palchksum = 0, length = 0; + char fname[MAX_PATH]; + char ident[MAX_PATH]; FILE *fp = nullptr; - wcstombs(fname, _ident.c_str(), MAX_PATH); - /* XXX case sensitivity fiasco! - * files must use _a, _rgb, _all, _allciByRGBA, _ciByRGBA, _ci - * and file extensions must be in lower case letters! */ -#ifdef OS_WINDOWS - { - unsigned int i; - for (i = 0; i < strlen(fname); i++) fname[i] = tolower(fname[i]); - } -#endif - ident.assign(fname); + wcstombs(ident, _ident.c_str(), MAX_PATH); + wcstombs(fname, foundfilename, MAX_PATH); + + /* lowercase on windows */ + CORRECTFILENAME(ident); + CORRECTFILENAME(fname); /* read in Rice's file naming convention */ -#define CRCFMTSIZ_LEN 13 -#define CRCWILDCARD_LEN 15 -#define PALCRC_LEN 9 - wcstombs(fname, foundfilename, MAX_PATH); - /* XXX case sensitivity fiasco! - * files must use _a, _rgb, _all, _allciByRGBA, _ciByRGBA, _ci - * and file extensions must be in lower case letters! */ -#ifdef OS_WINDOWS - { - unsigned int i; - for (i = 0; i < strlen(fname); i++) fname[i] = tolower(fname[i]); - } -#endif - pfname = fname + strlen(fname) - 4; - if (!(pfname == strstr(fname, ".png") || - pfname == strstr(fname, ".bmp") || - pfname == strstr(fname, ".dds"))) { -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); -#endif - INFO(80, wst("Error: not png or bmp or dds!\n")); - continue; - } - pfname = strstr(fname, ident.c_str()); - if (pfname != fname) pfname = 0; - if (pfname) { - uint32_t length = 0; - const char* strName = pfname + ident.size(); - - /* wildcard support */ - if (strchr(strName, '$')) { - if (sscanf(strName, "#%08X#%01X#%01X#$", &chksum, &fmt, &siz) == 3) { - length = CRCWILDCARD_LEN; - } else if (sscanf(strName, "#$#%01X#%01X#%08X", &fmt, &siz, &palchksum) == 3) { - length = CRCWILDCARD_LEN; - } - - hasWildcard = (length != 0); - } else { - if (sscanf(strName, "#%08X#%01X#%01X#%08X", &chksum, &fmt, &siz, &palchksum) == 4) { - length = CRCFMTSIZ_LEN + PALCRC_LEN; - } else if (sscanf(strName, "#%08X#%01X#%01X", &chksum, &fmt, &siz) == 3) { - length = CRCFMTSIZ_LEN; - } - } - - if (length) { - pfname += (ident.size() + length); - } else { - pfname = 0; - } - } - - if (!pfname) { -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n", dir_path)); - INFO(80, wst("file: %ls\n", foundfilename)); -#endif - INFO(80, wst("Error: not Rice texture naming convention!\n")); - continue; - } - if (!chksum && !hasWildcard) { -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); -#endif - INFO(80, wst("Error: crc32 = 0!\n")); + length = checkFileName(ident, fname, &chksum, &palchksum, &fmt, &siz); + if (length == 0) { + /* invalid file name, skip it */ continue; } @@ -339,424 +254,22 @@ TxHiResCache::LoadResult TxHiResCache::loadHiResTextures(const wchar_t * dir_pat if (isCached(chksum64)) { #if !DEBUG INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); + INFO(80, wst("file: %s\n"), fname); #endif INFO(80, wst("Error: already cached! duplicate texture!\n")); continue; } } + tex = loadFileInfoTex(fname, siz, &width, &height, fmt, &format); + if (tex == nullptr) { + /* failed to load file into tex data, skip it */ + continue; + } + DBG_INFO(80, wst("rom: %ls chksum:%08X %08X fmt:%x size:%x\n"), _ident.c_str(), chksum, palchksum, fmt, siz); - /* Deal with the wackiness some texture packs utilize Rice format. - * Read in the following order: _a.* + _rgb.*, _all.png _ciByRGBA.png, - * _allciByRGBA.png, and _ci.bmp. PNG are prefered over BMP. - * - * For some reason there are texture packs that include them all. Some - * even have RGB textures named as _all.* and ARGB textures named as - * _rgb.*... Someone pleeeez write a GOOD guideline for the texture - * designers!!! - * - * We allow hires textures to have higher bpp than the N64 originals. - */ - /* N64 formats - * Format: 0 - RGBA, 1 - YUV, 2 - CI, 3 - IA, 4 - I - * Size: 0 - 4bit, 1 - 8bit, 2 - 16bit, 3 - 32 bit - */ - - /* - * read in _rgb.* and _a.* - */ - if (pfname == strstr(fname, "_rgb.") || pfname == strstr(fname, "_a.")) { - strcpy(pfname, "_rgb.png"); - if (!osal_path_existsA(fname)) { - strcpy(pfname, "_rgb.bmp"); - if (!osal_path_existsA(fname)) { -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); -#endif - INFO(80, wst("Error: missing _rgb.*! _a.* must be paired with _rgb.*!\n")); - continue; - } - } - /* _a.png */ - strcpy(pfname, "_a.png"); - if ((fp = fopen(fname, "rb")) != nullptr) { - tmptex = _txImage->readPNG(fp, &tmpwidth, &tmpheight, &tmpformat); - fclose(fp); - } - if (!tmptex) { - /* _a.bmp */ - strcpy(pfname, "_a.bmp"); - if ((fp = fopen(fname, "rb")) != nullptr) { - tmptex = _txImage->readBMP(fp, &tmpwidth, &tmpheight, &tmpformat); - fclose(fp); - } - } - /* _rgb.png */ - strcpy(pfname, "_rgb.png"); - if ((fp = fopen(fname, "rb")) != nullptr) { - tex = _txImage->readPNG(fp, &width, &height, &format); - fclose(fp); - } - if (!tex) { - /* _rgb.bmp */ - strcpy(pfname, "_rgb.bmp"); - if ((fp = fopen(fname, "rb")) != nullptr) { - tex = _txImage->readBMP(fp, &width, &height, &format); - fclose(fp); - } - } - if (tmptex) { - /* check if _rgb.* and _a.* have matching size and format. */ - if (!tex || width != tmpwidth || height != tmpheight || - format != graphics::internalcolorFormat::RGBA8 || tmpformat != graphics::internalcolorFormat::RGBA8) { -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); -#endif - if (!tex) { - INFO(80, wst("Error: missing _rgb.*!\n")); - } - else if (width != tmpwidth || height != tmpheight) { - INFO(80, wst("Error: _rgb.* and _a.* have mismatched width or height!\n")); - } - else if (format != graphics::internalcolorFormat::RGBA8 || tmpformat != graphics::internalcolorFormat::RGBA8) { - INFO(80, wst("Error: _rgb.* or _a.* not in 32bit color!\n")); - } - if (tex) free(tex); - free(tmptex); - tex = nullptr; - tmptex = nullptr; - continue; - } - } - /* make adjustments */ - if (tex) { - if (tmptex) { - /* merge (A)RGB and A comp */ - DBG_INFO(80, wst("merge (A)RGB and A comp\n")); - int i; - for (i = 0; i < height * width; i++) { -#if 1 - /* use R comp for alpha. this is what Rice uses. sigh... */ - ((uint32*)tex)[i] &= 0x00ffffff; - ((uint32*)tex)[i] |= ((((uint32*)tmptex)[i] & 0xff) << 24); -#endif -#if 0 - /* use libpng style grayscale conversion */ - uint32 texel = ((uint32*)tmptex)[i]; - uint32 acomp = (((texel >> 16) & 0xff) * 6969 + - ((texel >> 8) & 0xff) * 23434 + - ((texel ) & 0xff) * 2365) / 32768; - ((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff); -#endif -#if 0 - /* use the standard NTSC gray scale conversion */ - uint32 texel = ((uint32*)tmptex)[i]; - uint32 acomp = (((texel >> 16) & 0xff) * 299 + - ((texel >> 8) & 0xff) * 587 + - ((texel ) & 0xff) * 114) / 1000; - ((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff); -#endif - } - free(tmptex); - tmptex = nullptr; - } - else { - /* clobber A comp. never a question of alpha. only RGB used. */ -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); -#endif - INFO(80, wst("Warning: missing _a.*! only using _rgb.*. treat as opaque texture.\n")); - int i; - for (i = 0; i < height * width; i++) { - ((uint32*)tex)[i] |= 0xff000000; - } - } - } - } - else - - /* - * read in _all.png, _all.dds, _allciByRGBA.png, _allciByRGBA.dds - * _ciByRGBA.png, _ciByRGBA.dds, _ci.bmp - */ - if (pfname == strstr(fname, "_all.png") || - pfname == strstr(fname, "_all.dds") || -#ifdef OS_WINDOWS - pfname == strstr(fname, "_allcibyrgba.png") || - pfname == strstr(fname, "_allcibyrgba.dds") || - pfname == strstr(fname, "_cibyrgba.png") || - pfname == strstr(fname, "_cibyrgba.dds") || -#else - pfname == strstr(fname, "_allciByRGBA.png") || - pfname == strstr(fname, "_allciByRGBA.dds") || - pfname == strstr(fname, "_ciByRGBA.png") || - pfname == strstr(fname, "_ciByRGBA.dds") || -#endif - pfname == strstr(fname, "_ci.bmp")) { - if ((fp = fopen(fname, "rb")) != nullptr) { - if (strstr(fname, ".png")) - tex = _txImage->readPNG(fp, &width, &height, &format); - else - tex = _txImage->readBMP(fp, &width, &height, &format); - fclose(fp); - } - } - - /* if we do not have a texture at this point we are screwed */ - if (!tex) { -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); -#endif - INFO(80, wst("Error: load failed!\n")); - continue; - } - DBG_INFO(80, wst("read in as %d x %d gfmt:%x\n"), tmpwidth, tmpheight, tmpformat); - - /* check if size and format are OK */ - if (!(format == graphics::internalcolorFormat::RGBA8 || format == graphics::internalcolorFormat::COLOR_INDEX8) || - (width * height) < 4) { /* TxQuantize requirement: width * height must be 4 or larger. */ - free(tex); - tex = nullptr; -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); -#endif - INFO(80, wst("Error: not width * height > 4 or 8bit palette color or 32bpp or dxt1 or dxt3 or dxt5!\n")); - continue; - } - - /* analyze and determine best format to quantize */ - if (format == graphics::internalcolorFormat::RGBA8) { - int i; - int alphabits = 0; - int fullalpha = 0; - boolean intensity = 1; - - if (!(getOptions() & LET_TEXARTISTS_FLY)) { - /* HACK ALERT! */ - /* Account for Rice's weirdness with fmt:0 siz:2 textures. - * Although the conditions are relaxed with other formats, - * the D3D RGBA5551 surface is used for this format in certain - * cases. See Nintemod's SuperMario64 life gauge and power - * meter. The same goes for fmt:2 textures. See Mollymutt's - * PaperMario text. */ - if ((fmt == 0 && siz == 2) || fmt == 2) { - DBG_INFO(80, wst("Remove black, white, etc borders along the alpha edges.\n")); - /* round A comp */ - for (i = 0; i < height * width; i++) { - uint32 texel = ((uint32*)tex)[i]; - ((uint32*)tex)[i] = ((texel & 0xff000000) == 0xff000000 ? 0xff000000 : 0) | - (texel & 0x00ffffff); - } - /* Substitute texel color with the average of the surrounding - * opaque texels. This removes borders regardless of hardware - * texture filtering (bilinear, etc). */ - int j; - for (i = 0; i < height; i++) { - for (j = 0; j < width; j++) { - uint32 texel = ((uint32*)tex)[i * width + j]; - if ((texel & 0xff000000) != 0xff000000) { - uint32 tmptexel[8]; - uint32 k, numtexel, r, g, b; - numtexel = r = g = b = 0; - memset(&tmptexel, 0, sizeof(tmptexel)); - if (i > 0) { - tmptexel[0] = ((uint32*)tex)[(i - 1) * width + j]; /* north */ - if (j > 0) tmptexel[1] = ((uint32*)tex)[(i - 1) * width + j - 1]; /* north-west */ - if (j < width - 1) tmptexel[2] = ((uint32*)tex)[(i - 1) * width + j + 1]; /* north-east */ - } - if (i < height - 1) { - tmptexel[3] = ((uint32*)tex)[(i + 1) * width + j]; /* south */ - if (j > 0) tmptexel[4] = ((uint32*)tex)[(i + 1) * width + j - 1]; /* south-west */ - if (j < width - 1) tmptexel[5] = ((uint32*)tex)[(i + 1) * width + j + 1]; /* south-east */ - } - if (j > 0) tmptexel[6] = ((uint32*)tex)[i * width + j - 1]; /* west */ - if (j < width - 1) tmptexel[7] = ((uint32*)tex)[i * width + j + 1]; /* east */ - for (k = 0; k < 8; k++) { - if ((tmptexel[k] & 0xff000000) == 0xff000000) { - b += ((tmptexel[k] & 0x00ff0000) >> 16); - g += ((tmptexel[k] & 0x0000ff00) >> 8); - r += ((tmptexel[k] & 0x000000ff)); - numtexel++; - } - } - if (numtexel) { - ((uint32*)tex)[i * width + j] = ((b / numtexel) << 16) | - ((g / numtexel) << 8) | - ((r / numtexel)); - } - else { - ((uint32*)tex)[i * width + j] = texel & 0x00ffffff; - } - } - } - } - } - } - - /* simple analysis of texture */ - for (i = 0; i < height * width; i++) { - uint32 texel = ((uint32*)tex)[i]; - if (alphabits != 8) { -#if AGGRESSIVE_QUANTIZATION - if ((texel & 0xff000000) < 0x00000003) { - alphabits = 1; - fullalpha++; - } else if ((texel & 0xff000000) < 0xfe000000) { - alphabits = 8; - } -#else - if ((texel & 0xff000000) == 0x00000000) { - alphabits = 1; - fullalpha++; - } else if ((texel & 0xff000000) != 0xff000000) { - alphabits = 8; - } -#endif - } - if (intensity) { - int rcomp = (texel >> 16) & 0xff; - int gcomp = (texel >> 8) & 0xff; - int bcomp = (texel)& 0xff; -#if AGGRESSIVE_QUANTIZATION - if (abs(rcomp - gcomp) > 8 || abs(rcomp - bcomp) > 8 || abs(gcomp - bcomp) > 8) - intensity = 0; -#else - if (rcomp != gcomp || rcomp != bcomp || gcomp != bcomp) intensity = 0; -#endif - } - if (!intensity && alphabits == 8) - break; - } - DBG_INFO(80, wst("required alpha bits:%d zero acomp texels:%d rgb as intensity:%d\n"), alphabits, fullalpha, intensity); - - /* preparations based on above analysis */ - if (_maxbpp < 32 || getOptions() & FORCE16BPP_HIRESTEX) { - if (alphabits == 0) - destformat = graphics::internalcolorFormat::RGB8; - else if (alphabits == 1) - destformat = graphics::internalcolorFormat::RGB5_A1; - else - destformat = graphics::internalcolorFormat::RGBA8; - } - else { - destformat = graphics::internalcolorFormat::RGBA8; - } - if (fmt == 4 && alphabits == 0) { - destformat = graphics::internalcolorFormat::RGBA8; - /* Rice I format; I = (R + G + B) / 3 */ - for (i = 0; i < height * width; i++) { - uint32 texel = ((uint32*)tex)[i]; - uint32 icomp = (((texel >> 16) & 0xff) + - ((texel >> 8) & 0xff) + - ((texel)& 0xff)) / 3; - ((uint32*)tex)[i] = (icomp << 24) | (texel & 0x00ffffff); - } - } - - DBG_INFO(80, wst("best gfmt:%x\n"), u32(destformat)); - } - /* - * Rice hi-res textures: end */ - - - /* XXX: only RGBA8888 for now. comeback to this later... */ - if (format == graphics::internalcolorFormat::RGBA8) { - - /* minification */ - if (width > _maxwidth || height > _maxheight) { - int ratio = 1; - if (width / _maxwidth > height / _maxheight) { - ratio = (int)ceil((double)width / _maxwidth); - } - else { - ratio = (int)ceil((double)height / _maxheight); - } - if (!_txReSample->minify(&tex, &width, &height, ratio)) { - free(tex); - tex = nullptr; - DBG_INFO(80, wst("Error: minification failed!\n")); - continue; - } - } - -#if POW2_TEXTURES -#if (POW2_TEXTURES == 2) - /* 3dfx Glide3x aspect ratio (8:1 - 1:8) */ - if (!_txReSample->nextPow2(&tex, &width , &height, 32, 1)) { -#else - /* normal pow2 expansion */ - if (!_txReSample->nextPow2(&tex, &width , &height, 32, 0)) { -#endif - free(tex); - tex = nullptr; - DBG_INFO(80, wst("Error: aspect ratio adjustment failed!\n")); - continue; - } -#endif - - /* quantize */ - { - tmptex = (uint8 *)malloc(TxUtil::sizeofTx(width, height, destformat)); - if (tmptex == nullptr) { - free(tex); - tex = nullptr; - result = resError; - break; - } - if (destformat == graphics::internalcolorFormat::RGBA8 || - destformat == graphics::internalcolorFormat::RGBA4) { - if (_maxbpp < 32 || getOptions() & FORCE16BPP_HIRESTEX) - destformat = graphics::internalcolorFormat::RGBA4; - } - else if (destformat == graphics::internalcolorFormat::RGB5_A1) { - if (_maxbpp < 32 || getOptions() & FORCE16BPP_HIRESTEX) - destformat = graphics::internalcolorFormat::RGB5_A1; - } - if (_txQuantize->quantize(tex, tmptex, width, height, graphics::internalcolorFormat::RGBA8, destformat, 0)) { - format = destformat; - free(tex); - tex = tmptex; - } - else - free(tmptex); - tmptex = nullptr; - } - } - - - /* last minute validations */ - if (!tex || (!chksum && !hasWildcard) || !width || !height || format == graphics::internalcolorFormat::NOCOLOR || width > _maxwidth || height > _maxheight) { -#if !DEBUG - INFO(80, wst("-----\n")); - INFO(80, wst("path: %ls\n"), dir_path.string().c_str()); - INFO(80, wst("file: %ls\n"), it->path().leaf().c_str()); -#endif - if (tex) { - free(tex); - tex = nullptr; - INFO(80, wst("Error: bad format or size! %d x %d gfmt:%x\n"), width, height, u32(format)); - } - else { - INFO(80, wst("Error: load failed!!\n")); - } - continue; - } - /* load it into hires texture cache. */ - { uint64 chksum64 = (uint64)palchksum; if (chksum) { chksum64 <<= 32; @@ -792,12 +305,27 @@ TxHiResCache::LoadResult TxHiResCache::loadHiResTextures(const wchar_t * dir_pat result = resError; break; } - } } while (foundfilename != nullptr); + osal_search_dir_close(dir); CHDIR(curpath); return result; } + +bool TxHiResCache::empty() const +{ + return TxCache::empty(); +} + +bool TxHiResCache::add(Checksum checksum, GHQTexInfo *info, int dataSize) +{ + return TxCache::add(checksum, info, dataSize); +} + +bool TxHiResCache::get(Checksum checksum, GHQTexInfo *info) +{ + return TxCache::get(checksum, info); +} diff --git a/src/GLideNHQ/TxHiResCache.h b/src/GLideNHQ/TxHiResCache.h index 1326ce9a..41b884ee 100644 --- a/src/GLideNHQ/TxHiResCache.h +++ b/src/GLideNHQ/TxHiResCache.h @@ -24,38 +24,29 @@ #ifndef __TXHIRESCACHE_H__ #define __TXHIRESCACHE_H__ -/* support hires textures - * 0: disable - * 1: enable - */ -#define HIRES_TEXTURE 1 - #include "TxCache.h" #include "TxQuantize.h" #include "TxImage.h" #include "TxReSample.h" +#include "TxHiResLoader.h" -class TxHiResCache : public TxCache +class TxHiResCache : public TxCache, public TxHiResLoader { private: - int _maxwidth; - int _maxheight; - int _maxbpp; bool _abortLoad; bool _cacheDumped; - std::unique_ptr _txImage; - std::unique_ptr _txQuantize; - std::unique_ptr _txReSample; + tx_wstring _texPackPath; enum LoadResult { resOk, resNotFound, resError }; - LoadResult loadHiResTextures(const wchar_t * dir_path, boolean replace); + LoadResult _loadHiResTextures(const wchar_t * dir_path, boolean replace); boolean _HiResTexPackPathExists() const; tx_wstring _getFileName() const override; int _getConfig() const override; + bool _load(boolean replace); public: ~TxHiResCache(); @@ -67,8 +58,11 @@ public: const wchar_t *texPackPath, const wchar_t *ident, dispInfoFuncExt callback); - bool load(boolean replace); - void dump(); + bool empty() const override; + bool add(Checksum checksum, GHQTexInfo *info, int dataSize = 0) override; + bool get(Checksum checksum, GHQTexInfo *info) override; + bool reload() override; + void dump() override; }; #endif /* __TXHIRESCACHE_H__ */ diff --git a/src/GLideNHQ/TxHiResLoader.cpp b/src/GLideNHQ/TxHiResLoader.cpp new file mode 100644 index 00000000..17b21515 --- /dev/null +++ b/src/GLideNHQ/TxHiResLoader.cpp @@ -0,0 +1,585 @@ +#include "TxHiResLoader.h" +#include "TxDbg.h" +#include "TxDbg.h" +#include "Ext_TxFilter.h" + +#include +#include +#include + +/* use power of 2 texture size + * (0:disable, 1:enable, 2:3dfx) */ +#define POW2_TEXTURES 0 + +/* use aggressive format assumption for quantization + * (0:disable, 1:enable, 2:extreme) */ +#define AGGRESSIVE_QUANTIZATION 1 + +TxHiResLoader::TxHiResLoader(int maxwidth, + int maxheight, + int maxbpp, + int options) + : _txImage(new TxImage()) + , _txQuantize(new TxQuantize()) + , _txReSample(new TxReSample()) + , _maxwidth(maxwidth) + , _maxheight(maxheight) + , _maxbpp(maxbpp) + , _options(options) +{ + +} + +uint32_t TxHiResLoader::checkFileName(char* ident, char* filename, + uint32_t* pChksum, uint32_t* pPalchksum, + uint32_t* pFmt, uint32_t* pSiz) +{ +#define CRCFMTSIZ_LEN 13 +#define CRCWILDCARD_LEN 15 +#define PALCRC_LEN 9 + + const char* strName; + const char* pfilename; + uint32_t length = 0, filename_type = 0; + bool hasWildcard = false; + const char supported_ends[][20] = { + "all.png", + "all.dds", +#ifdef OS_WINDOWS + "allcibyrgba.png", + "allcibyrgba.dds", + "cibyrgba.png", + "cibyrgba.dds", +#else + "allciByRGBA.png", + "allciByRGBA.dds", + "ciByRGBA.png", + "ciByRGBA.dds", +#endif + "rgb.png", + "rgb.bmp", + "a.png", + "a.bmp" + }; + + pfilename = filename + strlen(filename) - 4; + + if (strcmp(pfilename, ".png") && + strcmp(pfilename, ".bmp") && + strcmp(pfilename, ".dds")) { +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %s\n"), filename); +#endif + INFO(80, wst("Error: not png or bmp or dds!\n")); + return 0; + } + + /* make sure filename contains ident */ + pfilename = strstr(filename, ident); + if (!pfilename) { + return 0; + } + + strName = pfilename + strlen(ident); + + /* wildcard support */ + if (strchr(strName, '$')) { + if (sscanf(strName, "#%08X#%01X#%01X#$", pChksum, pFmt, pSiz) == 3) { + filename_type = 1; + length = CRCWILDCARD_LEN; + } else if (sscanf(strName, "#$#%01X#%01X#%08X", pFmt, pSiz, pPalchksum) == 3) { + filename_type = 2; + length = CRCWILDCARD_LEN; + } + + hasWildcard = (length != 0); + } else { + if (sscanf(strName, "#%08X#%01X#%01X#%08X", pChksum, pFmt, pSiz, pPalchksum) == 4) { + filename_type = 3; + length = CRCFMTSIZ_LEN + PALCRC_LEN; + } else if (sscanf(strName, "#%08X#%01X#%01X", pChksum, pFmt, pSiz) == 3) { + filename_type = 4; + length = CRCFMTSIZ_LEN; + } + } + + /* try to re-create string and match it */ + bool supportedFilename = false; + char test_filename[MAX_PATH]; + for (int i = 0; length && i < (sizeof(supported_ends) / sizeof(supported_ends[0])); i++) { + char* end = (char*)supported_ends[i]; + + switch (filename_type) + { + default: + case 1: + sprintf(test_filename, "%s#%08X#%01X#%01X#$_%s", ident, *pChksum, *pFmt, *pSiz, end); + break; + case 2: + sprintf(test_filename, "%s#$#%01X#%01X#%08X_%s", ident, *pFmt, *pSiz, *pPalchksum, end); + break; + case 3: + sprintf(test_filename, "%s#%08X#%01X#%01X#%08X_%s", ident, *pChksum, *pFmt, *pSiz, *pPalchksum, end); + break; + case 4: + sprintf(test_filename, "%s#%08X#%01X#%01X_%s", ident, *pChksum, *pFmt, *pSiz, end); + break; + } + + /* lowercase on windows */ + CORRECTFILENAME(test_filename); + + /* when it matches, break */ + if (strcmp(test_filename, filename) == 0) { + supportedFilename = true; + break; + } + } + + if (!supportedFilename || !length) { +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %s\n", filename)); +#endif + INFO(80, wst("Error: not Rice texture naming convention!\n")); + return 0; + } + + if (!*pChksum && !hasWildcard) { +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %s\n"), filename); +#endif + INFO(80, wst("Error: crc32 = 0!\n")); + return 0; + } + + return length; +} + +uint8_t* TxHiResLoader::loadFileInfoTex(char* fname, + int siz, int* pWidth, int* pHeight, + uint32_t fmt, + ColorFormat* pFormat) +{ + /* Deal with the wackiness some texture packs utilize Rice format. + * Read in the following order: _a.* + _rgb.*, _all.png _ciByRGBA.png, + * _allciByRGBA.png, and _ci.bmp. PNG are prefered over BMP. + * + * For some reason there are texture packs that include them all. Some + * even have RGB textures named as _all.* and ARGB textures named as + * _rgb.*... Someone pleeeez write a GOOD guideline for the texture + * designers!!! + * + * We allow hires textures to have higher bpp than the N64 originals. + */ + /* N64 formats + * Format: 0 - RGBA, 1 - YUV, 2 - CI, 3 - IA, 4 - I + * Size: 0 - 4bit, 1 - 8bit, 2 - 16bit, 3 - 32 bit + */ + + uint8_t* tex = nullptr; + uint8_t* tmptex = nullptr; + int tmpwidth = 0, tmpheight = 0; + int width = 0, height = 0; + FILE* fp = nullptr; + + ColorFormat tmpformat = graphics::internalcolorFormat::NOCOLOR; + ColorFormat destformat = graphics::internalcolorFormat::NOCOLOR; + ColorFormat format = graphics::internalcolorFormat::NOCOLOR; + + char* pfname; + + /* + * read in _rgb.* and _a.* + */ + if ((pfname = strstr(fname, "_rgb.")) || + (pfname = strstr(fname, "_a."))) { + strcpy(pfname, "_rgb.png"); + if (!osal_path_existsA(fname)) { + strcpy(pfname, "_rgb.bmp"); + if (!osal_path_existsA(fname)) { +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %s\n"), fname); +#endif + INFO(80, wst("Error: missing _rgb.*! _a.* must be paired with _rgb.*!\n")); + return nullptr;; + } + } + /* _a.png */ + strcpy(pfname, "_a.png"); + if ((fp = fopen(fname, "rb")) != nullptr) { + tmptex = _txImage->readPNG(fp, &tmpwidth, &tmpheight, &tmpformat); + fclose(fp); + } + if (!tmptex) { + /* _a.bmp */ + strcpy(pfname, "_a.bmp"); + if ((fp = fopen(fname, "rb")) != nullptr) { + tmptex = _txImage->readBMP(fp, &tmpwidth, &tmpheight, &tmpformat); + fclose(fp); + } + } + /* _rgb.png */ + strcpy(pfname, "_rgb.png"); + if ((fp = fopen(fname, "rb")) != nullptr) { + tex = _txImage->readPNG(fp, &width, &height, &format); + fclose(fp); + } + if (!tex) { + /* _rgb.bmp */ + strcpy(pfname, "_rgb.bmp"); + if ((fp = fopen(fname, "rb")) != nullptr) { + tex = _txImage->readBMP(fp, &width, &height, &format); + fclose(fp); + } + } + if (tmptex) { + /* check if _rgb.* and _a.* have matching size and format. */ + if (!tex || width != tmpwidth || height != tmpheight || + format != graphics::internalcolorFormat::RGBA8 || tmpformat != graphics::internalcolorFormat::RGBA8) { +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %s\n"), fname); +#endif + if (!tex) { + INFO(80, wst("Error: missing _rgb.*!\n")); + } + else if (width != tmpwidth || height != tmpheight) { + INFO(80, wst("Error: _rgb.* and _a.* have mismatched width or height!\n")); + } + else if (format != graphics::internalcolorFormat::RGBA8 || tmpformat != graphics::internalcolorFormat::RGBA8) { + INFO(80, wst("Error: _rgb.* or _a.* not in 32bit color!\n")); + } + if (tex) free(tex); + free(tmptex); + tex = nullptr; + tmptex = nullptr; + return nullptr; + } + } + /* make adjustments */ + if (tex) { + if (tmptex) { + /* merge (A)RGB and A comp */ + DBG_INFO(80, wst("merge (A)RGB and A comp\n")); + int i; + for (i = 0; i < height * width; i++) { +#if 1 + /* use R comp for alpha. this is what Rice uses. sigh... */ + ((uint32*)tex)[i] &= 0x00ffffff; + ((uint32*)tex)[i] |= ((((uint32*)tmptex)[i] & 0xff) << 24); +#endif +#if 0 + /* use libpng style grayscale conversion */ + uint32 texel = ((uint32*)tmptex)[i]; + uint32 acomp = (((texel >> 16) & 0xff) * 6969 + + ((texel >> 8) & 0xff) * 23434 + + ((texel ) & 0xff) * 2365) / 32768; + ((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff); +#endif +#if 0 + /* use the standard NTSC gray scale conversion */ + uint32 texel = ((uint32*)tmptex)[i]; + uint32 acomp = (((texel >> 16) & 0xff) * 299 + + ((texel >> 8) & 0xff) * 587 + + ((texel ) & 0xff) * 114) / 1000; + ((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff); +#endif + } + free(tmptex); + tmptex = nullptr; + } + else { + /* clobber A comp. never a question of alpha. only RGB used. */ +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %ls\n"), fname); +#endif + INFO(80, wst("Warning: missing _a.*! only using _rgb.*. treat as opaque texture.\n")); + int i; + for (i = 0; i < height * width; i++) { + ((uint32*)tex)[i] |= 0xff000000; + } + } + } + } + else + /* + * read in _all.png, _all.dds, _allciByRGBA.png, _allciByRGBA.dds + * _ciByRGBA.png, _ciByRGBA.dds, _ci.bmp + */ + if (strstr(fname, "_all.png") || + strstr(fname, "_all.dds") || +#ifdef OS_WINDOWS + strstr(fname, "_allcibyrgba.png") || + strstr(fname, "_allcibyrgba.dds") || + strstr(fname, "_cibyrgba.png") || + strstr(fname, "_cibyrgba.dds") || +#else + strstr(fname, "_allciByRGBA.png") || + strstr(fname, "_allciByRGBA.dds") || + strstr(fname, "_ciByRGBA.png") || + strstr(fname, "_ciByRGBA.dds") || +#endif + strstr(fname, "_ci.bmp")) { + + if ((fp = fopen(fname, "rb")) != nullptr) { + if (strstr(fname, ".png")) + tex = _txImage->readPNG(fp, &width, &height, &format); + else + tex = _txImage->readBMP(fp, &width, &height, &format); + + fclose(fp); + } + } + + /* if we do not have a texture at this point we are screwed */ + if (!tex) { +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %s\n"), fname); +#endif + INFO(80, wst("Error: load failed!\n")); + return nullptr; + } + DBG_INFO(80, wst("read in as %d x %d gfmt:%x\n"), tmpwidth, tmpheight, tmpformat); + + /* check if size and format are OK */ + if (!(format == graphics::internalcolorFormat::RGBA8 || format == graphics::internalcolorFormat::COLOR_INDEX8) || + (width * height) < 4) { /* TxQuantize requirement: width * height must be 4 or larger. */ + free(tex); + tex = nullptr; +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %ls\n"), fname); +#endif + INFO(80, wst("Error: not width * height > 4 or 8bit palette color or 32bpp or dxt1 or dxt3 or dxt5!\n")); + return nullptr; + } + + /* analyze and determine best format to quantize */ + if (format == graphics::internalcolorFormat::RGBA8) { + int i; + int alphabits = 0; + int fullalpha = 0; + boolean intensity = 1; + + if (!(_options & LET_TEXARTISTS_FLY)) { + /* HACK ALERT! */ + /* Account for Rice's weirdness with fmt:0 siz:2 textures. + * Although the conditions are relaxed with other formats, + * the D3D RGBA5551 surface is used for this format in certain + * cases. See Nintemod's SuperMario64 life gauge and power + * meter. The same goes for fmt:2 textures. See Mollymutt's + * PaperMario text. */ + if ((fmt == 0 && siz == 2) || fmt == 2) { + DBG_INFO(80, wst("Remove black, white, etc borders along the alpha edges.\n")); + /* round A comp */ + for (i = 0; i < height * width; i++) { + uint32 texel = ((uint32*)tex)[i]; + ((uint32*)tex)[i] = ((texel & 0xff000000) == 0xff000000 ? 0xff000000 : 0) | + (texel & 0x00ffffff); + } + /* Substitute texel color with the average of the surrounding + * opaque texels. This removes borders regardless of hardware + * texture filtering (bilinear, etc). */ + int j; + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + uint32 texel = ((uint32*)tex)[i * width + j]; + if ((texel & 0xff000000) != 0xff000000) { + uint32 tmptexel[8]; + uint32 k, numtexel, r, g, b; + numtexel = r = g = b = 0; + memset(&tmptexel, 0, sizeof(tmptexel)); + if (i > 0) { + tmptexel[0] = ((uint32*)tex)[(i - 1) * width + j]; /* north */ + if (j > 0) tmptexel[1] = ((uint32*)tex)[(i - 1) * width + j - 1]; /* north-west */ + if (j < width - 1) tmptexel[2] = ((uint32*)tex)[(i - 1) * width + j + 1]; /* north-east */ + } + if (i < height - 1) { + tmptexel[3] = ((uint32*)tex)[(i + 1) * width + j]; /* south */ + if (j > 0) tmptexel[4] = ((uint32*)tex)[(i + 1) * width + j - 1]; /* south-west */ + if (j < width - 1) tmptexel[5] = ((uint32*)tex)[(i + 1) * width + j + 1]; /* south-east */ + } + if (j > 0) tmptexel[6] = ((uint32*)tex)[i * width + j - 1]; /* west */ + if (j < width - 1) tmptexel[7] = ((uint32*)tex)[i * width + j + 1]; /* east */ + for (k = 0; k < 8; k++) { + if ((tmptexel[k] & 0xff000000) == 0xff000000) { + b += ((tmptexel[k] & 0x00ff0000) >> 16); + g += ((tmptexel[k] & 0x0000ff00) >> 8); + r += ((tmptexel[k] & 0x000000ff)); + numtexel++; + } + } + if (numtexel) { + ((uint32*)tex)[i * width + j] = ((b / numtexel) << 16) | + ((g / numtexel) << 8) | + ((r / numtexel)); + } + else { + ((uint32*)tex)[i * width + j] = texel & 0x00ffffff; + } + } + } + } + } + } + + /* simple analysis of texture */ + for (i = 0; i < height * width; i++) { + uint32 texel = ((uint32*)tex)[i]; + if (alphabits != 8) { +#if AGGRESSIVE_QUANTIZATION + if ((texel & 0xff000000) < 0x00000003) { + alphabits = 1; + fullalpha++; + } else if ((texel & 0xff000000) < 0xfe000000) { + alphabits = 8; + } +#else + if ((texel & 0xff000000) == 0x00000000) { + alphabits = 1; + fullalpha++; + } else if ((texel & 0xff000000) != 0xff000000) { + alphabits = 8; + } +#endif + } + if (intensity) { + int rcomp = (texel >> 16) & 0xff; + int gcomp = (texel >> 8) & 0xff; + int bcomp = (texel)& 0xff; +#if AGGRESSIVE_QUANTIZATION + if (abs(rcomp - gcomp) > 8 || abs(rcomp - bcomp) > 8 || abs(gcomp - bcomp) > 8) + intensity = 0; +#else + if (rcomp != gcomp || rcomp != bcomp || gcomp != bcomp) intensity = 0; +#endif + } + if (!intensity && alphabits == 8) + break; + } + DBG_INFO(80, wst("required alpha bits:%d zero acomp texels:%d rgb as intensity:%d\n"), alphabits, fullalpha, intensity); + + /* preparations based on above analysis */ + if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX) { + if (alphabits == 0) + destformat = graphics::internalcolorFormat::RGB8; + else if (alphabits == 1) + destformat = graphics::internalcolorFormat::RGB5_A1; + else + destformat = graphics::internalcolorFormat::RGBA8; + } + else { + destformat = graphics::internalcolorFormat::RGBA8; + } + if (fmt == 4 && alphabits == 0) { + destformat = graphics::internalcolorFormat::RGBA8; + /* Rice I format; I = (R + G + B) / 3 */ + for (i = 0; i < height * width; i++) { + uint32 texel = ((uint32*)tex)[i]; + uint32 icomp = (((texel >> 16) & 0xff) + + ((texel >> 8) & 0xff) + + ((texel)& 0xff)) / 3; + ((uint32*)tex)[i] = (icomp << 24) | (texel & 0x00ffffff); + } + } + + DBG_INFO(80, wst("best gfmt:%x\n"), u32(destformat)); + } + /* + * Rice hi-res textures: end */ + + + /* XXX: only RGBA8888 for now. comeback to this later... */ + if (format == graphics::internalcolorFormat::RGBA8) { + + /* minification */ + if (width > _maxwidth || height > _maxheight) { + int ratio = 1; + if (width / _maxwidth > height / _maxheight) { + ratio = (int)ceil((double)width / _maxwidth); + } + else { + ratio = (int)ceil((double)height / _maxheight); + } + if (!_txReSample->minify(&tex, &width, &height, ratio)) { + free(tex); + tex = nullptr; + DBG_INFO(80, wst("Error: minification failed!\n")); + return nullptr; + } + } + +#if POW2_TEXTURES +#if (POW2_TEXTURES == 2) + /* 3dfx Glide3x aspect ratio (8:1 - 1:8) */ + if (!_txReSample->nextPow2(&tex, &width , &height, 32, 1)) { +#else + /* normal pow2 expansion */ + if (!_txReSample->nextPow2(&tex, &width , &height, 32, 0)) { +#endif + free(tex); + tex = nullptr; + DBG_INFO(80, wst("Error: aspect ratio adjustment failed!\n")); + return nullptr; + } +#endif + + /* quantize */ + { + tmptex = (uint8 *)malloc(TxUtil::sizeofTx(width, height, destformat)); + if (tmptex == nullptr) { + free(tex); + tex = nullptr; + return nullptr; + } + if (destformat == graphics::internalcolorFormat::RGBA8 || + destformat == graphics::internalcolorFormat::RGBA4) { + if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX) + destformat = graphics::internalcolorFormat::RGBA4; + } + else if (destformat == graphics::internalcolorFormat::RGB5_A1) { + if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX) + destformat = graphics::internalcolorFormat::RGB5_A1; + } + if (_txQuantize->quantize(tex, tmptex, width, height, graphics::internalcolorFormat::RGBA8, destformat, 0)) { + format = destformat; + free(tex); + tex = tmptex; + } + else + free(tmptex); + tmptex = nullptr; + } + } + + + /* last minute validations */ + if (!tex || !width || !height || format == graphics::internalcolorFormat::NOCOLOR || width > _maxwidth || height > _maxheight) { +#if !DEBUG + INFO(80, wst("-----\n")); + INFO(80, wst("file: %s\n"), fname); +#endif + if (tex) { + free(tex); + tex = nullptr; + INFO(80, wst("Error: bad format or size! %d x %d gfmt:%x\n"), width, height, u32(format)); + } + else { + INFO(80, wst("Error: load failed!!\n")); + } + return nullptr; + } + + *pWidth = width; + *pHeight = height; + *pFormat = format; + + return tex; +} + diff --git a/src/GLideNHQ/TxHiResLoader.h b/src/GLideNHQ/TxHiResLoader.h new file mode 100644 index 00000000..98c72f19 --- /dev/null +++ b/src/GLideNHQ/TxHiResLoader.h @@ -0,0 +1,50 @@ +#ifndef TXHIRESLOADER_H +#define TXHIRESLOADER_H + +/* support hires textures + * 0: disable + * 1: enable + */ +#define HIRES_TEXTURE 1 + +#ifdef OS_WINDOWS +#define CORRECTFILENAME(str) for (uint32 i = 0; i < strlen(str); i++) str[i] = tolower(str[i]) +#else +#define CORRECTFILENAME(str) +#endif /* OS_WINDOWS */ + +#include "TxCache.h" +#include "TxQuantize.h" +#include "TxImage.h" +#include "TxReSample.h" + +class TxHiResLoader +{ +protected: + uint32_t checkFileName(char* ident, char* fname, uint32_t* pChksum, uint32_t* pPalchksum, uint32_t* pFmt, uint32_t* pSiz); + uint8_t* loadFileInfoTex(char* fname, int siz, int* pWidth, int* pHeight, uint32_t fmt, ColorFormat* pFormat); + + std::unique_ptr _txImage; + std::unique_ptr _txQuantize; + std::unique_ptr _txReSample; + + int _maxwidth; + int _maxheight; + int _maxbpp; + int _options; + +public: + TxHiResLoader(int maxwidth, + int maxheight, + int maxbpp, + int options); + virtual ~TxHiResLoader(){} + + virtual bool empty() const = 0; + virtual bool add(Checksum checksum, GHQTexInfo *info, int dataSize = 0) = 0; + virtual bool get(Checksum checksum, GHQTexInfo *info) = 0; + virtual bool reload() = 0; + virtual void dump() = 0; +}; + +#endif /* TXHIRESLOADER_H */ diff --git a/src/GLideNHQ/TxHiResNoCache.cpp b/src/GLideNHQ/TxHiResNoCache.cpp new file mode 100644 index 00000000..351c835d --- /dev/null +++ b/src/GLideNHQ/TxHiResNoCache.cpp @@ -0,0 +1,225 @@ +#include "TxHiResNoCache.h" + +#include "TxDbg.h" +#include "Ext_TxFilter.h" +#include + +TxHiResNoCache::TxHiResNoCache(int maxwidth, + int maxheight, + int maxbpp, + int options, + const wchar_t *cachePath, + const wchar_t *texPackPath, + const wchar_t *fullTexPath, + const wchar_t *ident, + dispInfoFuncExt callback) + : TxHiResLoader(maxwidth, maxheight, maxbpp, options) + , _fullTexPath(fullTexPath) + , _ident(ident) + , _callback(callback) +{ + /* store this for _createFileIndexInDir */ + wcstombs(_identc, _ident.c_str(), MAX_PATH); + + /* lowercase on windows */ + CORRECTFILENAME(_identc); + + _createFileIndex(false); +} + +TxHiResNoCache::~TxHiResNoCache() +{ + _clear(); +} + +void TxHiResNoCache::_clear() +{ + /* free loaded textures */ + for (auto texMap : _loadedTex) { + free(texMap.second.data); + } + + /* clear all lists */ + _loadedTex.clear(); + _filesIndex.clear(); +} + +bool TxHiResNoCache::empty() const +{ + return _filesIndex.empty(); +} + +bool TxHiResNoCache::get(Checksum checksum, GHQTexInfo *info) +{ + if (!checksum) { + return false; + } + + +#ifdef DEBUG + uint32 chksum = checksum._checksum & 0xffffffff; + uint32 palchksum = checksum._checksum >> 32; +#endif + + /* loop over each file from the index and try to match it with checksum */ + auto indexEntry = _filesIndex.find(checksum); + if (indexEntry == _filesIndex.end()) { + DBG_INFO(80, wst("TxNoCache::get: chksum:%08X %08X not found\n"), chksum, palchksum); + return false; + } + + fileIndexEntry_t& entry = indexEntry->second; + + /* make sure to not load the same texture twice */ + auto loadedTexMap = _loadedTex.find(checksum); + if (loadedTexMap != _loadedTex.end()) { + DBG_INFO(80, wst("TxNoCache::get: cached chksum:%08X %08X found\n"), chksum, palchksum); + *info = loadedTexMap->second; + return true; + } + + DBG_INFO(80, wst("TxNoCache::get: loading chksum:%08X %08X\n"), chksum, palchksum); + + /* change current dir to directory */ +#ifdef OS_WINDOWS + wchar_t curpath[MAX_PATH]; + GETCWD(MAX_PATH, curpath); + CHDIR(entry.directory.c_str()); +#else + char curpath[MAX_PATH]; + char cbuf[MAX_PATH]; + wcstombs(cbuf, entry.directory.c_str(), MAX_PATH); + GETCWD(MAX_PATH, curpath); + CHDIR(cbuf); +#endif + + /* load texture */ + int width = 0, height = 0; + ColorFormat format; + uint8_t* tex = TxHiResLoader::loadFileInfoTex(entry.fname, entry.siz, &width, &height, entry.fmt, &format); + + /* restore directory */ + CHDIR(curpath); + + if (tex == nullptr) { + /* failed to load texture, so return false */ + DBG_INFO(80, wst("TxNoCache::get: failed to load chksum:%08X %08X\n"), chksum, palchksum); + return false; + } + + DBG_INFO(80, wst("TxNoCache::get: loaded chksum:%08X %08X\n"), chksum, palchksum); + + info->data = tex; + info->width = width; + info->height = height; + info->is_hires_tex = 1; + setTextureFormat(format, info); + + /* add to loaded textures */ + _loadedTex.insert(std::map::value_type(checksum, *info)); + return true; +} + +bool TxHiResNoCache::reload() +{ + _clear(); + return _createFileIndex(true); +} + +bool TxHiResNoCache::_createFileIndex(bool update) +{ + /* don't display anything during an update, + * it causes flicker on i.e an ssd + */ + if (!update && _callback) { + _callback(L"CREATING FILE INDEX. PLEASE WAIT..."); + } + + _createFileIndexInDir(_fullTexPath, update); + + return true; +} + +bool TxHiResNoCache::_createFileIndexInDir(tx_wstring directory, bool update) +{ + /* find it on disk */ + if (!osal_path_existsW(directory.c_str())) { + return false; + } + + void *dir = osal_search_dir_open(directory.c_str()); + const wchar_t *foundfilename; + tx_wstring texturefilename; + bool result = true; + + do { + foundfilename = osal_search_dir_read_next(dir); + if (foundfilename == nullptr) { + /* no more files/directories */ + break; + } + + /* skip hidden files */ + if (wccmp(foundfilename, wst("."))) { + continue; + } + + texturefilename.assign(directory); + texturefilename += OSAL_DIR_SEPARATOR_STR; + texturefilename += foundfilename; + + /* recursive read into sub-directory */ + if (osal_is_directory(texturefilename.c_str())) { + result = _createFileIndexInDir(texturefilename.c_str(), update); + if (result) { + continue; + } else { + break; + } + } + + uint64 chksum64 = 0; + uint32 chksum = 0, palchksum = 0, length = 0; + fileIndexEntry_t entry; + entry.fmt = entry.siz = 0; + bool ret = false; + + wcstombs(entry.fname, foundfilename, MAX_PATH); + + /* lowercase on windows */ + CORRECTFILENAME(entry.fname); + + /* read in Rice's file naming convention */ + length = TxHiResLoader::checkFileName(_identc, entry.fname, &chksum, &palchksum, &entry.fmt, &entry.siz); + if (length == 0) { + /* invalid file name, skip it */ + continue; + } + + entry.directory = directory; + + chksum64 = (uint64)palchksum; + if (chksum) { + chksum64 <<= 32; + chksum64 |= (uint64)chksum; + } + + /* try to add entry to file index */ + ret = _filesIndex.insert(std::map::value_type(chksum64, entry)).second; + if (!ret) { + /* technically we should probably fail here, + * however HTS & HTC both don't fail when there are duplicates, + * so to maintain backwards compatability, we won't either + */ + DBG_INFO(80, wst("TxNoCache::_createFileIndexInDir: failed to add cksum:%08X %08X file:%ls\n"), chksum, palchksum, texturefilename.c_str()); + } else { + DBG_INFO(80, wst("TxNoCache::_createFileIndexInDir: added cksum:%08X %08X file:%ls\n"), chksum, palchksum, texturefilename.c_str()); + } + + } while (foundfilename != nullptr); + + osal_search_dir_close(dir); + + return result; +} + diff --git a/src/GLideNHQ/TxHiResNoCache.h b/src/GLideNHQ/TxHiResNoCache.h new file mode 100644 index 00000000..8baeabd4 --- /dev/null +++ b/src/GLideNHQ/TxHiResNoCache.h @@ -0,0 +1,45 @@ +#ifndef TXHIRESNOCACHE_H +#define TXHIRESNOCACHE_H + +#include "TxHiResLoader.h" + +class TxHiResNoCache : public TxHiResLoader +{ + private: + bool _createFileIndex(bool update); + bool _createFileIndexInDir(tx_wstring directory, bool update); + void _clear(); + + struct fileIndexEntry_t + { + char fname[MAX_PATH]; + tx_wstring directory; + uint32 siz; + uint32 fmt; + }; + + tx_wstring _fullTexPath; + tx_wstring _ident; + char _identc[MAX_PATH]; + std::map _filesIndex; + std::map _loadedTex; + dispInfoFuncExt _callback; + public: + ~TxHiResNoCache(); + TxHiResNoCache(int maxwidth, + int maxheight, + int maxbpp, + int options, + const wchar_t *cachePath, + const wchar_t *texPackPath, + const wchar_t *fullTexPath, + const wchar_t *ident, + dispInfoFuncExt callback); + bool empty() const override; + bool add(Checksum checksum, GHQTexInfo *info, int dataSize = 0) override { return false; } + bool get(Checksum checksum, GHQTexInfo *info) override; + bool reload() override; + void dump() override { }; +}; + +#endif /* TXHIRESNOCACHE_H */ diff --git a/src/GLideNHQ/TxInternal.h b/src/GLideNHQ/TxInternal.h index 7a387aa5..f70509cf 100644 --- a/src/GLideNHQ/TxInternal.h +++ b/src/GLideNHQ/TxInternal.h @@ -29,12 +29,6 @@ #include -#ifdef WIN32 -#define KBHIT(key) ((GetAsyncKeyState(key) & 0x8001) == 0x8001) -#else -#define KBHIT(key) (0) -#endif - typedef graphics::InternalColorFormatParam ColorFormat; /* in-memory zlib texture compression */ diff --git a/src/GLideNUI/ConfigDialog.cpp b/src/GLideNUI/ConfigDialog.cpp index 99548a98..ee5d040e 100644 --- a/src/GLideNUI/ConfigDialog.cpp +++ b/src/GLideNUI/ConfigDialog.cpp @@ -340,6 +340,7 @@ void ConfigDialog::_init(bool reInit, bool blockCustomSettings) ui->saveTextureCacheCheckBox->setChecked(config.textureFilter.txSaveCache != 0); ui->enhancedTexFileStorageCheckBox->setChecked(config.textureFilter.txEnhancedTextureFileStorage != 0); ui->hiresTexFileStorageCheckBox->setChecked(config.textureFilter.txHiresTextureFileStorage != 0); + ui->noTexFileStorageCheckBox->setChecked(config.textureFilter.txNoTextureFileStorage != 0); ui->texPackPathLineEdit->setText(QString::fromWCharArray(config.textureFilter.txPath)); ui->texCachePathLineEdit->setText(QString::fromWCharArray(config.textureFilter.txCachePath)); @@ -644,6 +645,7 @@ void ConfigDialog::accept(bool justSave) { config.textureFilter.txSaveCache = ui->saveTextureCacheCheckBox->isChecked() ? 1 : 0; config.textureFilter.txEnhancedTextureFileStorage = ui->enhancedTexFileStorageCheckBox->isChecked() ? 1 : 0; config.textureFilter.txHiresTextureFileStorage = ui->hiresTexFileStorageCheckBox->isChecked() ? 1 : 0; + config.textureFilter.txNoTextureFileStorage = ui->noTexFileStorageCheckBox->isChecked() ? 1 : 0; QDir txPath(ui->texPackPathLineEdit->text()); if (!txPath.exists() && @@ -853,6 +855,11 @@ void ConfigDialog::on_texDumpPathButton_clicked() ui->texDumpPathLineEdit->setText(directory); } +void ConfigDialog::on_noTexFileStorageCheckBox_toggled(bool checked) +{ + ui->hiresTexFileStorageCheckBox->setEnabled(!checked); +} + void ConfigDialog::on_windowedResolutionComboBox_currentIndexChanged(int index) { if (index < numWindowedModes) diff --git a/src/GLideNUI/ConfigDialog.h b/src/GLideNUI/ConfigDialog.h index 77f83adf..a85c0c77 100644 --- a/src/GLideNUI/ConfigDialog.h +++ b/src/GLideNUI/ConfigDialog.h @@ -74,6 +74,8 @@ private slots: void on_texDumpPathButton_clicked(); + void on_noTexFileStorageCheckBox_toggled(bool checked); + void on_profilesComboBox_currentIndexChanged(const QString &arg1); void on_settingsDestProfileRadioButton_toggled(bool checked); diff --git a/src/GLideNUI/Settings.cpp b/src/GLideNUI/Settings.cpp index 1a9c36e6..b374fa66 100644 --- a/src/GLideNUI/Settings.cpp +++ b/src/GLideNUI/Settings.cpp @@ -102,6 +102,7 @@ void _loadSettings(QSettings & settings) config.textureFilter.txSaveCache = settings.value("txSaveCache", config.textureFilter.txSaveCache).toInt(); config.textureFilter.txEnhancedTextureFileStorage = settings.value("txEnhancedTextureFileStorage", config.textureFilter.txEnhancedTextureFileStorage).toInt(); config.textureFilter.txHiresTextureFileStorage = settings.value("txHiresTextureFileStorage", config.textureFilter.txHiresTextureFileStorage).toInt(); + config.textureFilter.txNoTextureFileStorage = settings.value("txNoTextureFileStorage", config.textureFilter.txNoTextureFileStorage).toInt(); QString txPath = QString::fromWCharArray(config.textureFilter.txPath); config.textureFilter.txPath[settings.value("txPath", txPath).toString().toWCharArray(config.textureFilter.txPath)] = L'\0'; QString txCachePath = QString::fromWCharArray(config.textureFilter.txCachePath); @@ -289,6 +290,7 @@ void writeSettings(const QString & _strIniFolder) settings.setValue("txSaveCache", config.textureFilter.txSaveCache); settings.setValue("txEnhancedTextureFileStorage", config.textureFilter.txEnhancedTextureFileStorage); settings.setValue("txHiresTextureFileStorage", config.textureFilter.txHiresTextureFileStorage); + settings.setValue("txNoTextureFileStorage", config.textureFilter.txNoTextureFileStorage); settings.setValue("txPath", QString::fromWCharArray(config.textureFilter.txPath)); settings.setValue("txCachePath", QString::fromWCharArray(config.textureFilter.txCachePath)); settings.setValue("txDumpPath", QString::fromWCharArray(config.textureFilter.txDumpPath)); @@ -484,6 +486,7 @@ void saveCustomRomSettings(const QString & _strIniFolder, const char * _strRomNa WriteCustomSetting(textureFilter, txCacheSize); WriteCustomSetting(textureFilter, txEnhancedTextureFileStorage); WriteCustomSetting(textureFilter, txHiresTextureFileStorage); + WriteCustomSetting(textureFilter, txNoTextureFileStorage); WriteCustomSetting(textureFilter, txHiresEnable); WriteCustomSetting(textureFilter, txHiresFullAlphaChannel); WriteCustomSetting(textureFilter, txHresAltCRC); diff --git a/src/GLideNUI/configDialog.ui b/src/GLideNUI/configDialog.ui index bf9a0e4d..b0b50476 100644 --- a/src/GLideNUI/configDialog.ui +++ b/src/GLideNUI/configDialog.ui @@ -2659,6 +2659,13 @@ + + + + Use no file storage or cache (causes worse stutter, not recommended for playing games) + + + diff --git a/src/TextureFilterHandler.cpp b/src/TextureFilterHandler.cpp index c97b5673..a06e54f8 100644 --- a/src/TextureFilterHandler.cpp +++ b/src/TextureFilterHandler.cpp @@ -63,6 +63,8 @@ u32 TextureFilterHandler::_getConfigOptions() const options |= FILE_TEXCACHE; if (config.textureFilter.txHiresTextureFileStorage) options |= FILE_HIRESTEXCACHE; + if (config.textureFilter.txNoTextureFileStorage) + options |= FILE_NOTEXCACHE; return options; } diff --git a/src/common/CommonAPIImpl_common.cpp b/src/common/CommonAPIImpl_common.cpp index 70f3eee9..8e4f6728 100644 --- a/src/common/CommonAPIImpl_common.cpp +++ b/src/common/CommonAPIImpl_common.cpp @@ -198,6 +198,8 @@ void PluginAPI::RomClosed() int PluginAPI::RomOpen() { + osal_keys_init(); + LOG(LOG_APIFUNC, "RomOpen"); #ifdef RSPTHREAD m_pluginThreadMtx.lock(); @@ -212,7 +214,6 @@ int PluginAPI::RomOpen() if (!dwnd().start()) return 0; #endif - osal_keys_init(); m_bRomOpen = true; diff --git a/src/mupenplus/Config_mupenplus.cpp b/src/mupenplus/Config_mupenplus.cpp index 30ae6b48..b497fd17 100644 --- a/src/mupenplus/Config_mupenplus.cpp +++ b/src/mupenplus/Config_mupenplus.cpp @@ -255,6 +255,8 @@ bool Config_SetDefault() assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultBool(g_configVideoGliden64, "txHiresTextureFileStorage", config.textureFilter.txHiresTextureFileStorage, "Use file storage instead of memory cache for HD textures."); assert(res == M64ERR_SUCCESS); + res = ConfigSetDefaultBool(g_configVideoGliden64, "txNoTextureFileStorage", config.textureFilter.txNoTextureFileStorage, "Use no file storage or cache for HD textures."); + assert(res == M64ERR_SUCCESS); // Convert to multibyte char txPath[PLUGIN_PATH_SIZE * 2]; wcstombs(txPath, config.textureFilter.txPath, PLUGIN_PATH_SIZE * 2); @@ -465,6 +467,8 @@ void Config_LoadCustomConfig() if (result == M64ERR_SUCCESS) config.textureFilter.txEnhancedTextureFileStorage = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txHiresTextureFileStorage", value, sizeof(value)); if (result == M64ERR_SUCCESS) config.textureFilter.txHiresTextureFileStorage = atoi(value); + result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txNoTextureFileStorage", value, sizeof(value)); + if (result == M64ERR_SUCCESS) config.textureFilter.txNoTextureFileStorage = atoi(value); ConfigExternalClose(fileHandle); } @@ -553,6 +557,7 @@ void Config_LoadConfig() config.textureFilter.txSaveCache = ConfigGetParamBool(g_configVideoGliden64, "txSaveCache"); config.textureFilter.txEnhancedTextureFileStorage = ConfigGetParamBool(g_configVideoGliden64, "txEnhancedTextureFileStorage"); config.textureFilter.txHiresTextureFileStorage = ConfigGetParamBool(g_configVideoGliden64, "txHiresTextureFileStorage"); + config.textureFilter.txNoTextureFileStorage = ConfigGetParamBool(g_configVideoGliden64, "txNoTextureFileStorage"); ::mbstowcs(config.textureFilter.txPath, ConfigGetParamString(g_configVideoGliden64, "txPath"), PLUGIN_PATH_SIZE); ::mbstowcs(config.textureFilter.txCachePath, ConfigGetParamString(g_configVideoGliden64, "txCachePath"), PLUGIN_PATH_SIZE); ::mbstowcs(config.textureFilter.txDumpPath, ConfigGetParamString(g_configVideoGliden64, "txDumpPath"), PLUGIN_PATH_SIZE);