diff --git a/DDrawCompat/Common/Log.cpp b/DDrawCompat/Common/Log.cpp index 844bc59..c13ca69 100644 --- a/DDrawCompat/Common/Log.cpp +++ b/DDrawCompat/Common/Log.cpp @@ -16,6 +16,16 @@ namespace namespace Compat { + std::string getTrimmedTypeName(const std::string& typeName) + { + std::string prefix("struct "); + if (prefix == typeName.substr(0, prefix.length())) + { + return typeName.substr(prefix.length()); + } + return typeName; + } + LogStream operator<<(LogStream os, const void* ptr) { if (ptr) diff --git a/DDrawCompat/Common/Log.h b/DDrawCompat/Common/Log.h index 260ad7a..36af951 100644 --- a/DDrawCompat/Common/Log.h +++ b/DDrawCompat/Common/Log.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,14 @@ namespace Compat return val.elem; } + std::string getTrimmedTypeName(const std::string& typeName); + + template + std::string getTypeName() + { + return getTrimmedTypeName(typeid(T).name()); + } + template Hex hex(T val) { diff --git a/DDrawCompat/Common/VtableHookVisitor.h b/DDrawCompat/Common/VtableHookVisitor.h index 4fa8ef1..40308f9 100644 --- a/DDrawCompat/Common/VtableHookVisitor.h +++ b/DDrawCompat/Common/VtableHookVisitor.h @@ -68,16 +68,6 @@ public: } private: - static std::string getVtableTypeName() - { - std::string name = typeid(Vtable).name(); - if (0 == name.find("struct ")) - { - name = name.substr(name.find(" ") + 1); - } - return name; - } - template static Result STDMETHODCALLTYPE hookFunc(FirstParam firstParam, Params... params) { @@ -107,4 +97,4 @@ template std::string VtableHookVisitor::s_funcName; template -std::string VtableHookVisitor::s_vtableTypeName(getVtableTypeName()); +std::string VtableHookVisitor::s_vtableTypeName(Compat::getTypeName()); diff --git a/DDrawCompat/Config/Config.cpp b/DDrawCompat/Config/Config.cpp index a65ae6b..2906fca 100644 --- a/DDrawCompat/Config/Config.cpp +++ b/DDrawCompat/Config/Config.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,7 @@ namespace Config Settings::SpriteTexCoord spriteTexCoord; Settings::StatsHotKey statsHotKey; Settings::SupportedResolutions supportedResolutions; + Settings::SupportedTextureFormats supportedTextureFormats; Settings::TerminateHotKey terminateHotKey; Settings::TextureFilter textureFilter; Settings::ThreadPriorityBoost threadPriorityBoost; diff --git a/DDrawCompat/Config/Parser.cpp b/DDrawCompat/Config/Parser.cpp index 18aebad..a37e141 100644 --- a/DDrawCompat/Config/Parser.cpp +++ b/DDrawCompat/Config/Parser.cpp @@ -209,6 +209,16 @@ namespace Config return result; } + std::string toupper(const std::string& str) + { + std::string result(str); + for (auto& c : result) + { + c = std::toupper(c, std::locale()); + } + return result; + } + std::string trim(const std::string& str) { auto result(str); diff --git a/DDrawCompat/Config/Parser.h b/DDrawCompat/Config/Parser.h index f43d518..35973a0 100644 --- a/DDrawCompat/Config/Parser.h +++ b/DDrawCompat/Config/Parser.h @@ -25,6 +25,7 @@ namespace Config void registerSetting(Setting& setting); std::string removeParam(const std::string& value); std::string tolower(const std::string& str); + std::string toupper(const std::string& str); std::string trim(const std::string& str); } } diff --git a/DDrawCompat/Config/Settings/CpuAffinity.cpp b/DDrawCompat/Config/Settings/CpuAffinity.cpp index 85c834c..a70a4c4 100644 --- a/DDrawCompat/Config/Settings/CpuAffinity.cpp +++ b/DDrawCompat/Config/Settings/CpuAffinity.cpp @@ -59,6 +59,10 @@ namespace Config unsigned result = 0; for (const auto& value : values) { + if ("app" == value || "all" == value) + { + throw ParsingError("'" + value + "' cannot be combined with other values"); + } auto num = Parser::parseInt(value, 1, 32); result |= 1U << (num - 1); } diff --git a/DDrawCompat/Config/Settings/SupportedTextureFormats.cpp b/DDrawCompat/Config/Settings/SupportedTextureFormats.cpp new file mode 100644 index 0000000..326cd3d --- /dev/null +++ b/DDrawCompat/Config/Settings/SupportedTextureFormats.cpp @@ -0,0 +1,148 @@ +#include +#include + +#include +#include + +#include +#include + +namespace +{ + std::map g_formatNames = []() + { + std::map names; + +#define ADD_FORMAT(format) names[format] = #format + ADD_FORMAT(D3DDDIFMT_R8G8B8); + ADD_FORMAT(D3DDDIFMT_A8R8G8B8); + ADD_FORMAT(D3DDDIFMT_X8R8G8B8); + ADD_FORMAT(D3DDDIFMT_R5G6B5); + ADD_FORMAT(D3DDDIFMT_X1R5G5B5); + ADD_FORMAT(D3DDDIFMT_A1R5G5B5); + ADD_FORMAT(D3DDDIFMT_A4R4G4B4); + ADD_FORMAT(D3DDDIFMT_R3G3B2); + ADD_FORMAT(D3DDDIFMT_A8); + ADD_FORMAT(D3DDDIFMT_A8R3G3B2); + ADD_FORMAT(D3DDDIFMT_X4R4G4B4); + ADD_FORMAT(D3DDDIFMT_A8P8); + ADD_FORMAT(D3DDDIFMT_P8); + ADD_FORMAT(D3DDDIFMT_L8); + ADD_FORMAT(D3DDDIFMT_A8L8); + ADD_FORMAT(D3DDDIFMT_A4L4); + ADD_FORMAT(D3DDDIFMT_V8U8); + ADD_FORMAT(D3DDDIFMT_L6V5U5); + ADD_FORMAT(D3DDDIFMT_X8L8V8U8); +#undef ADD_FORMAT + + for (auto& pair : names) + { + pair.second = Config::Parser::tolower(pair.second.substr(10)); + } + return names; + }(); + +#define FOURCC(cc) *reinterpret_cast(#cc) + std::map> g_formatGroups = { + { "argb", { D3DDDIFMT_A8R8G8B8, D3DDDIFMT_A1R5G5B5, D3DDDIFMT_A4R4G4B4 } }, + { "bump", { D3DDDIFMT_V8U8, D3DDDIFMT_L6V5U5, D3DDDIFMT_X8L8V8U8 } }, + { "dxt", { FOURCC(DXT1), FOURCC(DXT2), FOURCC(DXT3), FOURCC(DXT4),FOURCC(DXT5) } }, + { "lum", { D3DDDIFMT_L8, D3DDDIFMT_A8L8, D3DDDIFMT_A4L4 } }, + { "rgb", { D3DDDIFMT_X8R8G8B8, D3DDDIFMT_R5G6B5, D3DDDIFMT_X1R5G5B5, D3DDDIFMT_X4R4G4B4 } } + }; +#undef FOURCC +} + +namespace Config +{ + namespace Settings + { + SupportedTextureFormats::SupportedTextureFormats() + : ListSetting("SupportedTextureFormats", "all") + { + } + + std::string SupportedTextureFormats::getValueStr() const + { + if (m_formats.empty()) + { + return "all"; + } + + std::string result; + for (const auto& format : m_formats) + { + result += ", "; + auto it = g_formatNames.find(static_cast(format)); + if (it != g_formatNames.end()) + { + result += Config::Parser::toupper(it->second); + } + else + { + auto p = reinterpret_cast(&format); + result += std::string(p, p + 4); + } + } + return result.substr(2); + } + + bool SupportedTextureFormats::isSupported(UINT format) const + { + if (m_formats.empty()) + { + return true; + } + return m_formats.find(format) != m_formats.end(); + } + + void SupportedTextureFormats::setValues(const std::vector& values) + { + if (values.empty()) + { + throw ParsingError("empty list is not allowed"); + } + + if (1 == values.size() && "all" == values[0]) + { + m_formats.clear(); + return; + } + + std::set formats; + for (const auto& fmt : values) + { + if ("all" == fmt) + { + throw ParsingError("'all' cannot be combined with other values"); + } + + auto group = g_formatGroups.find(fmt); + if (group != g_formatGroups.end()) + { + formats.insert(group->second.begin(), group->second.end()); + continue; + } + + auto name = std::find_if(g_formatNames.begin(), g_formatNames.end(), + [&](const auto& pair) { return pair.second == fmt; }); + if (name != g_formatNames.end()) + { + formats.insert(name->first); + continue; + } + + if (4 == fmt.length() && + fmt.end() == std::find_if(fmt.begin(), fmt.end(), [](const char c) { return !std::isalnum(c); })) + { + formats.insert(*reinterpret_cast(Config::Parser::toupper(fmt).c_str())); + continue; + } + + throw ParsingError("invalid format name: '" + fmt + "'"); + } + + m_formats = formats; + } + } +} diff --git a/DDrawCompat/Config/Settings/SupportedTextureFormats.h b/DDrawCompat/Config/Settings/SupportedTextureFormats.h new file mode 100644 index 0000000..ebdd439 --- /dev/null +++ b/DDrawCompat/Config/Settings/SupportedTextureFormats.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +#include + +namespace Config +{ + namespace Settings + { + class SupportedTextureFormats : public ListSetting + { + public: + SupportedTextureFormats(); + + virtual std::string getValueStr() const override; + + bool isSupported(UINT format) const; + + private: + void setValues(const std::vector& values) override; + + std::set m_formats; + }; + } + + extern Settings::SupportedTextureFormats supportedTextureFormats; +} diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index bef6fca..3d8905e 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -187,6 +187,7 @@ + @@ -333,6 +334,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 0b7a0cc..8fe57d3 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -636,6 +636,9 @@ Header Files\DDraw + + Header Files\Config\Settings + @@ -1004,6 +1007,9 @@ Source Files\DDraw + + Source Files\Config\Settings + diff --git a/DDrawCompat/Direct3d/Direct3dDevice.cpp b/DDrawCompat/Direct3d/Direct3dDevice.cpp index 57e4836..fa44015 100644 --- a/DDrawCompat/Direct3d/Direct3dDevice.cpp +++ b/DDrawCompat/Direct3d/Direct3dDevice.cpp @@ -1,7 +1,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -10,6 +13,80 @@ namespace { + struct EnumTextureFormatsArgs + { + void* callback; + void* context; + }; + + bool isSupported(const DDPIXELFORMAT& pf) + { + if ((pf.dwFlags & DDPF_FOURCC) && pf.dwFourCC < 0x100) + { + // D3DDDIFMT_A8B8G8R8 and D3DDDIFMT_X8B8G8R8 are enumerated like this, but nobody is expected to use them, + // and with proper pixel formats these cannot be created in video memory anyway. + return false; + } + return Config::supportedTextureFormats.isSupported(D3dDdi::getFormat(pf)); + } + + bool isSupported(const DDSURFACEDESC& desc) + { + return isSupported(desc.ddpfPixelFormat); + } + + template + HRESULT CALLBACK enumTextureFormatsCallback(Format* lpFormat, LPVOID lpContext) + { + if (!isSupported(*lpFormat)) + { + return D3DENUMRET_OK; + } + auto& args = *static_cast(lpContext); + auto origCallback = static_cast)>(args.callback); + return origCallback(lpFormat, args.context); + } + + template + D3DDEVICEDESC getCaps(TDirect3DDevice* This) + { + D3DDEVICEDESC hwDesc = {}; + hwDesc.dwSize = sizeof(hwDesc); + D3DDEVICEDESC helDesc = {}; + helDesc.dwSize = sizeof(helDesc); + getOrigVtable(This).GetCaps(This, &hwDesc, &helDesc); + return hwDesc; + } + + D3DDEVICEDESC7 getCaps(IDirect3DDevice7* This) + { + D3DDEVICEDESC7 desc = {}; + getOrigVtable(This).GetCaps(This, &desc); + return desc; + } + + template + bool isHalDevice(TDirect3DDevice* This) + { + return getCaps(This).dwDevCaps & D3DDEVCAPS_TEXTUREVIDEOMEMORY; + } + + template + HRESULT STDMETHODCALLTYPE enumTextureFormats(TDirect3DDevice* This, EnumProc* lpd3dEnumPixelProc, LPVOID lpArg) + { + if (!This || !lpd3dEnumPixelProc || !isHalDevice(This)) + { + if (This && lpd3dEnumPixelProc) + { + LOG_ONCE("Using feature: enumerating software texture formats via " << Compat::getTypeName()); + } + return getOrigVtable(This).EnumTextureFormats(This, lpd3dEnumPixelProc, lpArg); + } + LOG_ONCE("Using feature: enumerating hardware texture formats via " << Compat::getTypeName()); + EnumTextureFormatsArgs args = { lpd3dEnumPixelProc, lpArg }; + return getOrigVtable(This).EnumTextureFormats(This, enumTextureFormatsCallback, &args); + } + HRESULT STDMETHODCALLTYPE execute(IDirect3DDevice* This, LPDIRECT3DEXECUTEBUFFER lpDirect3DExecuteBuffer, LPDIRECT3DVIEWPORT lpDirect3DViewport, DWORD dwFlags) { @@ -48,6 +125,8 @@ namespace { vtable.SetRenderTarget = &setRenderTarget; } + + vtable.EnumTextureFormats = &enumTextureFormats; } }