Allows tex clients decide how to handle invalid textures instead of assertions. Fixes #6436
This was SVN commit r26783.
This commit is contained in:
parent
8c1a469253
commit
d8d736f0eb
@ -29,6 +29,7 @@
|
||||
#include "ps/CStr.h"
|
||||
#include "ps/Profiler2.h"
|
||||
#include "ps/Threading.h"
|
||||
#include "ps/Util.h"
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
|
||||
#if CONFIG2_NVTT
|
||||
@ -342,9 +343,10 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
|
||||
}
|
||||
|
||||
Tex tex;
|
||||
if (tex.decode(file, fileSize) < 0)
|
||||
const Status decodeStatus = tex.decode(file, fileSize);
|
||||
if (decodeStatus != INFO::OK)
|
||||
{
|
||||
LOGERROR("Failed to decode texture \"%s\"", src.string8());
|
||||
LOGERROR("Failed to decode texture \"%s\" %s", src.string8(), GetStatusAsString(decodeStatus).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "ps/ConfigDB.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/Util.h"
|
||||
#include "ps/VideoMode.h"
|
||||
#include "renderer/backend/gl/Device.h"
|
||||
#include "renderer/Renderer.h"
|
||||
@ -401,12 +402,24 @@ public:
|
||||
|
||||
std::shared_ptr<u8> fileData;
|
||||
size_t fileSize;
|
||||
const Status loadStatus = g_VFS->LoadFile(path, fileData, fileSize);
|
||||
if (loadStatus != INFO::OK)
|
||||
{
|
||||
LOGERROR("Texture failed to load; \"%s\" %s",
|
||||
texture->m_Properties.m_Path.string8(), GetStatusAsString(loadStatus).c_str());
|
||||
texture->ResetBackendTexture(
|
||||
nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
|
||||
texture->m_TextureData.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
texture->m_TextureData = std::make_unique<Tex>();
|
||||
Tex& textureData = *texture->m_TextureData;
|
||||
if (g_VFS->LoadFile(path, fileData, fileSize) != INFO::OK ||
|
||||
textureData.decode(fileData, fileSize) != INFO::OK)
|
||||
const Status decodeStatus = textureData.decode(fileData, fileSize);
|
||||
if (decodeStatus != INFO::OK)
|
||||
{
|
||||
LOGERROR("Texture failed to load; \"%s\"", texture->m_Properties.m_Path.string8());
|
||||
LOGERROR("Texture failed to decode; \"%s\" %s",
|
||||
texture->m_Properties.m_Path.string8(), GetStatusAsString(decodeStatus).c_str());
|
||||
texture->ResetBackendTexture(
|
||||
nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
|
||||
texture->m_TextureData.reset();
|
||||
|
@ -34,7 +34,9 @@
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
static const StatusDefinition texStatusDefinitions[] = {
|
||||
static const StatusDefinition texStatusDefinitions[] =
|
||||
{
|
||||
{ ERR::TEX_UNKNOWN_FORMAT, L"Unknown texture format" },
|
||||
{ ERR::TEX_FMT_INVALID, L"Invalid/unsupported texture format" },
|
||||
{ ERR::TEX_INVALID_COLOR_TYPE, L"Invalid color type" },
|
||||
{ ERR::TEX_NOT_8BIT_PRECISION, L"Not 8-bit channel precision" },
|
||||
@ -55,7 +57,7 @@ STATUS_ADD_DEFINITIONS(texStatusDefinitions);
|
||||
Status Tex::validate() const
|
||||
{
|
||||
if(m_Flags & TEX_UNDEFINED_FLAGS)
|
||||
WARN_RETURN(ERR::_1);
|
||||
return ERR::_1;
|
||||
|
||||
// pixel data (only check validity if the image is still in memory).
|
||||
if(m_Data)
|
||||
@ -64,23 +66,23 @@ Status Tex::validate() const
|
||||
// possible causes: texture file header is invalid,
|
||||
// or file wasn't loaded completely.
|
||||
if(m_DataSize < m_Ofs + m_Width*m_Height*m_Bpp/8)
|
||||
WARN_RETURN(ERR::_2);
|
||||
return ERR::_2;
|
||||
}
|
||||
|
||||
// bits per pixel
|
||||
// (we don't bother checking all values; a sanity check is enough)
|
||||
if(m_Bpp % 4 || m_Bpp > 32)
|
||||
WARN_RETURN(ERR::_3);
|
||||
return ERR::_3;
|
||||
|
||||
// flags
|
||||
// .. DXT value
|
||||
const size_t dxt = m_Flags & TEX_DXT;
|
||||
if(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5)
|
||||
WARN_RETURN(ERR::_4);
|
||||
return ERR::_4;
|
||||
// .. orientation
|
||||
const size_t orientation = m_Flags & TEX_ORIENTATION;
|
||||
if(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN))
|
||||
WARN_RETURN(ERR::_5);
|
||||
return ERR::_5;
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
@ -718,10 +720,10 @@ Status Tex::decode(const std::shared_ptr<u8>& Data, size_t DataSize)
|
||||
// make sure the entire header is available
|
||||
const size_t min_hdr_size = c->hdr_size(0);
|
||||
if(DataSize < min_hdr_size)
|
||||
WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);
|
||||
return ERR::TEX_INCOMPLETE_HEADER;
|
||||
const size_t hdr_size = c->hdr_size(Data.get());
|
||||
if(DataSize < hdr_size)
|
||||
WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);
|
||||
return ERR::TEX_INCOMPLETE_HEADER;
|
||||
|
||||
m_Data = Data;
|
||||
m_DataSize = DataSize;
|
||||
@ -731,9 +733,9 @@ Status Tex::decode(const std::shared_ptr<u8>& Data, size_t DataSize)
|
||||
|
||||
// sanity checks
|
||||
if(!m_Width || !m_Height || m_Bpp > 32)
|
||||
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
||||
return ERR::TEX_FMT_INVALID;
|
||||
if(m_DataSize < m_Ofs + img_size())
|
||||
WARN_RETURN(ERR::TEX_INVALID_SIZE);
|
||||
return ERR::TEX_INVALID_SIZE;
|
||||
|
||||
flip_to_global_orientation(this);
|
||||
|
||||
|
@ -67,7 +67,7 @@ Status tex_codec_for_header(const u8* file, size_t file_size, const ITexCodec**
|
||||
{
|
||||
// we guarantee at least 4 bytes for is_hdr to look at
|
||||
if(file_size < 4)
|
||||
WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);
|
||||
return ERR::TEX_INCOMPLETE_HEADER;
|
||||
|
||||
for(int i = 0; i < codecs_len; ++i)
|
||||
{
|
||||
@ -78,7 +78,7 @@ Status tex_codec_for_header(const u8* file, size_t file_size, const ITexCodec**
|
||||
}
|
||||
}
|
||||
|
||||
WARN_RETURN(ERR::TEX_UNKNOWN_FORMAT);
|
||||
return ERR::TEX_UNKNOWN_FORMAT;
|
||||
}
|
||||
|
||||
Status tex_codec_transform(Tex* t, size_t transforms)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2020 Wildfire Games.
|
||||
/* Copyright (C) 2022 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -397,7 +397,7 @@ static Status decode_pf(const DDS_PIXELFORMAT* pf, size_t& bpp, size_t& flags)
|
||||
|
||||
// check struct size
|
||||
if(read_le32(&pf->dwSize) != sizeof(DDS_PIXELFORMAT))
|
||||
WARN_RETURN(ERR::TEX_INVALID_SIZE);
|
||||
return ERR::TEX_INVALID_SIZE;
|
||||
|
||||
// determine type
|
||||
const size_t pf_flags = (size_t)read_le32(&pf->dwFlags);
|
||||
@ -432,7 +432,7 @@ static Status decode_pf(const DDS_PIXELFORMAT* pf, size_t& bpp, size_t& flags)
|
||||
// DDS is storing images in a format that requires no processing,
|
||||
// we do not allow any weird orderings that require runtime work.
|
||||
// instead, the artists must export with the correct settings.
|
||||
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
||||
return ERR::TEX_FMT_INVALID;
|
||||
}
|
||||
|
||||
RETURN_STATUS_IF_ERR(tex_validate_plain_format(bpp, (int)flags));
|
||||
@ -446,10 +446,10 @@ static Status decode_pf(const DDS_PIXELFORMAT* pf, size_t& bpp, size_t& flags)
|
||||
bpp = pf_bpp;
|
||||
|
||||
if(pf_bpp != 8)
|
||||
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
||||
return ERR::TEX_FMT_INVALID;
|
||||
|
||||
if(pf_a_mask != 0xFF)
|
||||
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
||||
return ERR::TEX_FMT_INVALID;
|
||||
flags |= TEX_GREY;
|
||||
|
||||
RETURN_STATUS_IF_ERR(tex_validate_plain_format(bpp, (int)flags));
|
||||
@ -480,12 +480,12 @@ static Status decode_pf(const DDS_PIXELFORMAT* pf, size_t& bpp, size_t& flags)
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
||||
return ERR::TEX_FMT_INVALID;
|
||||
}
|
||||
}
|
||||
// .. neither uncompressed nor compressed - invalid
|
||||
else
|
||||
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
||||
return ERR::TEX_FMT_INVALID;
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
@ -499,7 +499,7 @@ static Status decode_sd(const DDS_HEADER* sd, size_t& w, size_t& h, size_t& bpp,
|
||||
{
|
||||
// check header size
|
||||
if(read_le32(&sd->dwSize) != sizeof(*sd))
|
||||
WARN_RETURN(ERR::CORRUPTED);
|
||||
return ERR::CORRUPTED;
|
||||
|
||||
// flags (indicate which fields are valid)
|
||||
const size_t sd_flags = (size_t)read_le32(&sd->dwFlags);
|
||||
@ -507,7 +507,7 @@ static Status decode_sd(const DDS_HEADER* sd, size_t& w, size_t& h, size_t& bpp,
|
||||
// note: we can't guess dimensions - the image may not be square.
|
||||
const size_t sd_req_flags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT;
|
||||
if((sd_flags & sd_req_flags) != sd_req_flags)
|
||||
WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);
|
||||
return ERR::TEX_INCOMPLETE_HEADER;
|
||||
|
||||
// image dimensions
|
||||
h = (size_t)read_le32(&sd->dwHeight);
|
||||
@ -537,7 +537,7 @@ static Status decode_sd(const DDS_HEADER* sd, size_t& w, size_t& h, size_t& bpp,
|
||||
if(sd_flags & DDSD_PITCH)
|
||||
{
|
||||
if(sd_pitch_or_size != Align<4>(pitch))
|
||||
DEBUG_WARN_ERR(ERR::CORRUPTED);
|
||||
return ERR::CORRUPTED;
|
||||
}
|
||||
if(sd_flags & DDSD_LINEARSIZE)
|
||||
{
|
||||
@ -545,7 +545,7 @@ static Status decode_sd(const DDS_HEADER* sd, size_t& w, size_t& h, size_t& bpp,
|
||||
// so allow values close to that as well
|
||||
const ssize_t totalSize = ssize_t(pitch*stored_h*1.333333f);
|
||||
if(sd_pitch_or_size != pitch*stored_h && std::abs(ssize_t(sd_pitch_or_size)-totalSize) > 64)
|
||||
DEBUG_WARN_ERR(ERR::CORRUPTED);
|
||||
return ERR::CORRUPTED;
|
||||
}
|
||||
// note: both flags set would be invalid; no need to check for that,
|
||||
// though, since one of the above tests would fail.
|
||||
@ -559,7 +559,7 @@ static Status decode_sd(const DDS_HEADER* sd, size_t& w, size_t& h, size_t& bpp,
|
||||
// mipmap chain is incomplete
|
||||
// note: DDS includes the base level in its count, hence +1.
|
||||
if(mipmap_count != ceil_log2(std::max(w,h))+1)
|
||||
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
||||
return ERR::TEX_FMT_INVALID;
|
||||
flags |= TEX_MIPMAPS;
|
||||
}
|
||||
}
|
||||
@ -569,17 +569,19 @@ static Status decode_sd(const DDS_HEADER* sd, size_t& w, size_t& h, size_t& bpp,
|
||||
{
|
||||
const size_t depth = (size_t)read_le32(&sd->dwDepth);
|
||||
if(depth)
|
||||
WARN_RETURN(ERR::NOT_SUPPORTED);
|
||||
return ERR::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// check caps
|
||||
// .. this is supposed to be set, but don't bail if not (pointless)
|
||||
ENSURE(sd->dwCaps & DDSCAPS_TEXTURE);
|
||||
if (!(sd->dwCaps & DDSCAPS_TEXTURE))
|
||||
return ERR::CORRUPTED;
|
||||
// .. sanity check: warn if mipmap flag not set (don't bail if not
|
||||
// because we've already made the decision).
|
||||
const bool mipmap_cap = (sd->dwCaps & DDSCAPS_MIPMAP) != 0;
|
||||
const bool mipmap_flag = (flags & TEX_MIPMAPS) != 0;
|
||||
ENSURE(mipmap_cap == mipmap_flag);
|
||||
if (mipmap_cap != mipmap_flag)
|
||||
return ERR::CORRUPTED;
|
||||
// note: we do not check for cubemaps and volume textures (not supported)
|
||||
// because the file may still have useful data we can read.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2018 Wildfire Games.
|
||||
/* Copyright (C) 2022 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -271,7 +271,7 @@ TIMER_ADD_CLIENT(tc_png_decode);
|
||||
// limitation: palette images aren't supported
|
||||
Status TexCodecPng::decode(u8* RESTRICT data, size_t size, Tex* RESTRICT t) const
|
||||
{
|
||||
TIMER_ACCRUE(tc_png_decode);
|
||||
TIMER_ACCRUE(tc_png_decode);
|
||||
|
||||
png_infop info_ptr = 0;
|
||||
|
||||
@ -279,19 +279,19 @@ TIMER_ACCRUE(tc_png_decode);
|
||||
// warning handler to filter out useless messages
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, user_warning_fn);
|
||||
if(!png_ptr)
|
||||
WARN_RETURN(ERR::FAIL);
|
||||
return ERR::FAIL;
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if(!info_ptr)
|
||||
{
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
|
||||
WARN_RETURN(ERR::NO_MEM);
|
||||
return ERR::NO_MEM;
|
||||
}
|
||||
// setup error handling
|
||||
if(setjmp(png_jmpbuf(png_ptr)))
|
||||
{
|
||||
// libpng longjmps here after an error
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
|
||||
WARN_RETURN(ERR::FAIL);
|
||||
return ERR::FAIL;
|
||||
}
|
||||
|
||||
MemoryStream stream(data, size);
|
||||
|
@ -144,7 +144,10 @@ const wchar_t* ErrorString(int err)
|
||||
// TODO: load from language file
|
||||
}
|
||||
|
||||
|
||||
CStr GetStatusAsString(Status status)
|
||||
{
|
||||
return utf8_from_wstring(ErrorString(status));
|
||||
}
|
||||
|
||||
// write the specified texture to disk.
|
||||
// note: <t> cannot be made const because the image may have to be
|
||||
|
@ -18,8 +18,10 @@
|
||||
#ifndef PS_UTIL_H
|
||||
#define PS_UTIL_H
|
||||
|
||||
#include "lib/os_path.h"
|
||||
#include "lib/file/vfs/vfs_path.h"
|
||||
#include "lib/os_path.h"
|
||||
#include "lib/status.h"
|
||||
#include "ps/CStr.h"
|
||||
|
||||
class Tex;
|
||||
|
||||
@ -27,6 +29,8 @@ void WriteSystemInfo();
|
||||
|
||||
const wchar_t* ErrorString(int err);
|
||||
|
||||
CStr GetStatusAsString(Status status);
|
||||
|
||||
OsPath createDateIndexSubdirectory(const OsPath& parentDir);
|
||||
|
||||
Status tex_write(Tex* t, const VfsPath& filename);
|
||||
|
@ -95,8 +95,12 @@ void SkyManager::LoadAndUploadSkyTexturesIfNeeded(
|
||||
}
|
||||
}
|
||||
|
||||
textures[i].decode(file, fileSize);
|
||||
textures[i].transform_to((textures[i].m_Flags | TEX_BOTTOM_UP | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS));
|
||||
if (textures[i].decode(file, fileSize) != INFO::OK ||
|
||||
textures[i].transform_to((textures[i].m_Flags | TEX_BOTTOM_UP | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) != INFO::OK)
|
||||
{
|
||||
LOGERROR("Error creating sky cubemap '%s', can't decode file: '%s'.", m_SkySet.ToUTF8().c_str(), path.string8().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_pow2(textures[i].m_Width) || !is_pow2(textures[i].m_Height))
|
||||
{
|
||||
|
@ -253,7 +253,7 @@ static void HandleError(const std::wstring& message, const VfsPath& pathname, St
|
||||
if (err == ERR::AGAIN)
|
||||
return;
|
||||
|
||||
LOGERROR("%s: pathname=%s, error=%s", utf8_from_wstring(message), pathname.string8(), utf8_from_wstring(ErrorString(err)));
|
||||
LOGERROR("%s: pathname=%s, error=%s", utf8_from_wstring(message), pathname.string8(), GetStatusAsString(err).c_str());
|
||||
}
|
||||
|
||||
void CSoundGroup::PlayNext(const CVector3D& position, entity_id_t source)
|
||||
|
Loading…
Reference in New Issue
Block a user