mirror of
https://github.com/blawar/GLideN64.git
synced 2024-06-30 08:24:05 +00:00
GLideNHQ: Introduce TxHiResNoCache
This commit is contained in:
parent
b78df0b002
commit
4a9e1260ce
|
@ -126,7 +126,9 @@
|
|||
<ClCompile Include="..\..\src\GLideNHQ\TxDbg.cpp" />
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxFilter.cpp" />
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxFilterExport.cpp" />
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxHiResLoader.cpp" />
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxHiResCache.cpp" />
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxHiResNoCache.cpp" />
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxImage.cpp" />
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxQuantize.cpp" />
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxReSample.cpp" />
|
||||
|
|
|
@ -22,9 +22,15 @@
|
|||
<ClCompile Include="..\..\src\GLideNHQ\TxFilterExport.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxHiResLoader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxHiResCache.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxHiResNoCache.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\GLideNHQ\TxImage.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 <osal_files.h>
|
||||
#include <osal_keys.h>
|
||||
#include <zlib.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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> _txImage;
|
||||
std::unique_ptr<TxQuantize> _txQuantize;
|
||||
std::unique_ptr<TxReSample> _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__ */
|
||||
|
|
585
src/GLideNHQ/TxHiResLoader.cpp
Normal file
585
src/GLideNHQ/TxHiResLoader.cpp
Normal file
|
@ -0,0 +1,585 @@
|
|||
#include "TxHiResLoader.h"
|
||||
#include "TxDbg.h"
|
||||
#include "TxDbg.h"
|
||||
#include "Ext_TxFilter.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <osal_files.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
50
src/GLideNHQ/TxHiResLoader.h
Normal file
50
src/GLideNHQ/TxHiResLoader.h
Normal file
|
@ -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> _txImage;
|
||||
std::unique_ptr<TxQuantize> _txQuantize;
|
||||
std::unique_ptr<TxReSample> _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 */
|
225
src/GLideNHQ/TxHiResNoCache.cpp
Normal file
225
src/GLideNHQ/TxHiResNoCache.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
#include "TxHiResNoCache.h"
|
||||
|
||||
#include "TxDbg.h"
|
||||
#include "Ext_TxFilter.h"
|
||||
#include <osal_files.h>
|
||||
|
||||
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<uint64, GHQTexInfo>::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<uint64, fileIndexEntry_t>::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;
|
||||
}
|
||||
|
45
src/GLideNHQ/TxHiResNoCache.h
Normal file
45
src/GLideNHQ/TxHiResNoCache.h
Normal file
|
@ -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<uint64, fileIndexEntry_t> _filesIndex;
|
||||
std::map<uint64, GHQTexInfo> _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 */
|
|
@ -29,12 +29,6 @@
|
|||
|
||||
#include <Graphics/Parameters.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define KBHIT(key) ((GetAsyncKeyState(key) & 0x8001) == 0x8001)
|
||||
#else
|
||||
#define KBHIT(key) (0)
|
||||
#endif
|
||||
|
||||
typedef graphics::InternalColorFormatParam ColorFormat;
|
||||
|
||||
/* in-memory zlib texture compression */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -2659,6 +2659,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="noTexFileStorageCheckBox">
|
||||
<property name="text">
|
||||
<string>Use no file storage or cache (causes worse stutter, not recommended for playing games)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user