1
0
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:
Rosalie Wanders 2021-08-01 13:22:18 +02:00 committed by Sergey Lipskiy
parent b78df0b002
commit 4a9e1260ce
22 changed files with 1027 additions and 556 deletions

View File

@ -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" />

View File

@ -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>

View File

@ -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"));

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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__ */

View 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;
}

View 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 */

View 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;
}

View 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 */

View File

@ -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 */

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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>

View File

@ -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;
}

View File

@ -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;

View File

@ -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);