862 lines
32 KiB
C++
862 lines
32 KiB
C++
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
|
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
|
// ******************************************************************
|
|
// *
|
|
// * .,-::::: .,:: .::::::::. .,:: .:
|
|
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
|
|
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
|
|
// * $$$ Y$$$P $$""""Y$$ Y$$$P
|
|
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
|
|
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
|
|
// *
|
|
// * Cxbx->Win32->CxbxKrnl->HLEIntercept.cpp
|
|
// *
|
|
// * This file is part of the Cxbx project.
|
|
// *
|
|
// * Cxbx and Cxbe are free software; you can redistribute them
|
|
// * and/or modify them under the terms of the GNU General Public
|
|
// * License as published by the Free Software Foundation; either
|
|
// * version 2 of the license, or (at your option) any later version.
|
|
// *
|
|
// * This program is distributed in the hope that it will be useful,
|
|
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// * GNU General Public License for more details.
|
|
// *
|
|
// * You should have recieved a copy of the GNU General Public License
|
|
// * along with this program; see the file COPYING.
|
|
// * If not, write to the Free Software Foundation, Inc.,
|
|
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
|
// *
|
|
// * (c) 2002-2003 Aaron Robinson <caustik@caustik.com>
|
|
// *
|
|
// * All rights reserved
|
|
// *
|
|
// ******************************************************************
|
|
|
|
#define LOG_PREFIX "HLE " // Intentional extra space to align on 4 characters
|
|
|
|
#include <cmath>
|
|
#include <iomanip> // For std::setfill and std::setw
|
|
#include "CxbxKrnl.h"
|
|
#include "Emu.h"
|
|
#include "EmuFS.h"
|
|
#include "EmuXTL.h"
|
|
#include "EmuShared.h"
|
|
#include "CxbxDebugger.h"
|
|
#pragma comment(lib, "XbSymbolDatabase.lib")
|
|
#include "../../import/XbSymbolDatabase/XbSymbolDatabase.h"
|
|
#include "HLEIntercept.h"
|
|
#include "xxhash32.h"
|
|
#include <Shlwapi.h>
|
|
#include <subhook.h>
|
|
|
|
inline void EmuInstallPatch(std::string FunctionName, xbaddr FunctionAddr, void *Patch);
|
|
|
|
#include <shlobj.h>
|
|
#include <unordered_map>
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
std::map<std::string, xbaddr> g_SymbolAddresses;
|
|
std::unordered_map<std::string, subhook::Hook> g_FunctionHooks;
|
|
bool g_HLECacheUsed = false;
|
|
|
|
// D3D build version
|
|
uint32 g_BuildVersion = 0;
|
|
|
|
bool bLLE_APU = false; // Set this to true for experimental APU (sound) LLE
|
|
bool bLLE_GPU = false; // Set this to true for experimental GPU (graphics) LLE
|
|
bool bLLE_USB = false; // Set this to true for experimental USB (input) LLE
|
|
bool bLLE_JIT = false; // Set this to true for experimental JIT
|
|
|
|
void* GetXboxFunctionPointer(std::string functionName)
|
|
{
|
|
if (g_FunctionHooks.find(functionName) != g_FunctionHooks.end()) {
|
|
return g_FunctionHooks[functionName].GetTrampoline();
|
|
}
|
|
|
|
// If we got here, the function wasn't patched, so we can just look it up the HLE cache
|
|
// and return the correct offset
|
|
auto symbol = g_SymbolAddresses.find(functionName);
|
|
if (symbol != g_SymbolAddresses.end()) {
|
|
return (void*)symbol->second;
|
|
}
|
|
|
|
// Finally, if none of the above were matched, return nullptr
|
|
return nullptr;
|
|
}
|
|
|
|
// NOTE: GetDetectedSymbolName do not get to be in XbSymbolDatabase, get symbol string in Cxbx project only.
|
|
std::string GetDetectedSymbolName(xbaddr address, int *symbolOffset)
|
|
{
|
|
std::string result = "";
|
|
int closestMatch = MAXINT;
|
|
|
|
for (auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) {
|
|
xbaddr symbolAddr = (*it).second;
|
|
if (symbolAddr == NULL)
|
|
continue;
|
|
|
|
if (symbolAddr <= address)
|
|
{
|
|
int distance = address - symbolAddr;
|
|
if (closestMatch > distance)
|
|
{
|
|
closestMatch = distance;
|
|
result = (*it).first;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (closestMatch < MAXINT)
|
|
{
|
|
*symbolOffset = closestMatch;
|
|
return result;
|
|
}
|
|
|
|
*symbolOffset = 0;
|
|
return "unknown";
|
|
}
|
|
|
|
// NOTE: GetEmuPatchAddr do not get to be in XbSymbolDatabase, perform patch check in Cxbx project only.
|
|
void *GetEmuPatchAddr(std::string aFunctionName)
|
|
{
|
|
std::string patchName = "XTL::EmuPatch_" + aFunctionName;
|
|
void* addr = GetProcAddress(GetModuleHandle(NULL), patchName.c_str());
|
|
return addr;
|
|
}
|
|
|
|
// NOTE: VerifySymbolAddressAgainstXRef do not get to be in XbSymbolDatabase, perform verification in Cxbx project only.
|
|
/*
|
|
bool VerifySymbolAddressAgainstXRef(char *SymbolName, xbaddr Address, int XRef)
|
|
{
|
|
// Temporary verification - is XREF_D3DTSS_TEXCOORDINDEX derived correctly?
|
|
// TODO : Remove this when XREF_D3DTSS_TEXCOORDINDEX derivation is deemed stable
|
|
xbaddr XRefAddr = XRefDataBase[XRef];
|
|
if (XRefAddr == Address)
|
|
return true;
|
|
|
|
if (XRefAddr == XREF_ADDR_DERIVE) {
|
|
printf("HLE: XRef #%d derived 0x%.08X -> %s\n", XRef, Address, SymbolName);
|
|
XRefDataBase[XRef] = Address;
|
|
return true;
|
|
}
|
|
|
|
// For XREF_D3DTSS_TEXCOORDINDEX, Kabuki Warriors hits this case
|
|
CxbxPopupMessage("Verification of %s failed : XREF was 0x%.8X while lookup gave 0x%.8X", SymbolName, XRefAddr, Address);
|
|
// For XREF_D3DTSS_TEXCOORDINDEX, Kabuki Warriors hits this case
|
|
return false;
|
|
}*/
|
|
|
|
// x1nixmzeng: Hack to notify CxbxDebugger of the HLECache file, which is currently a hashed XBE header AND stripped title (see EmuHLEIntercept)
|
|
class CxbxDebuggerScopedMessage
|
|
{
|
|
std::string& message;
|
|
|
|
CxbxDebuggerScopedMessage() = delete;
|
|
CxbxDebuggerScopedMessage(const CxbxDebuggerScopedMessage&) = delete;
|
|
public:
|
|
|
|
CxbxDebuggerScopedMessage(std::string& message_string)
|
|
: message(message_string)
|
|
{ }
|
|
|
|
~CxbxDebuggerScopedMessage()
|
|
{
|
|
if (CxbxDebugger::CanReport())
|
|
{
|
|
CxbxDebugger::ReportHLECacheFile(message.c_str());
|
|
}
|
|
}
|
|
};
|
|
|
|
void CDECL EmuOutputMessage(xb_output_message mFlag,
|
|
const char* message)
|
|
{
|
|
switch (mFlag) {
|
|
case XB_OUTPUT_MESSAGE_INFO: {
|
|
printf("%s\n", message);
|
|
break;
|
|
}
|
|
case XB_OUTPUT_MESSAGE_WARN: {
|
|
EmuWarning("%s", message);
|
|
break;
|
|
}
|
|
case XB_OUTPUT_MESSAGE_ERROR: {
|
|
CxbxKrnlCleanup("%s", message);
|
|
break;
|
|
}
|
|
case XB_OUTPUT_MESSAGE_DEBUG:
|
|
default: {
|
|
#ifdef _DEBUG_TRACE
|
|
printf("%s\n", message);
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDECL EmuRegisterSymbol(const char* library_str,
|
|
uint32_t library_flag,
|
|
const char* symbol_str,
|
|
uint32_t func_addr,
|
|
uint32_t revision)
|
|
{
|
|
// Ignore registered symbol in current database.
|
|
uint32_t hasSymbol = g_SymbolAddresses[symbol_str];
|
|
if (hasSymbol != 0)
|
|
return;
|
|
|
|
// Output some details
|
|
std::stringstream output;
|
|
output << "HLE: 0x" << std::setfill('0') << std::setw(8) << std::hex << func_addr
|
|
<< " -> " << symbol_str << " " << std::dec << revision;
|
|
|
|
#if 0 // TODO: XbSymbolDatabase - Need to create a structure for patch and stuff.
|
|
bool IsXRef = OovpaTable->Oovpa->XRefSaveIndex != XRefNoSaveIndex;
|
|
if (IsXRef) {
|
|
output << "\t(XREF)";
|
|
|
|
// do we need to save the found address?
|
|
OOVPA* Oovpa = OovpaTable->Oovpa;
|
|
if (Oovpa->XRefSaveIndex != XRefNoSaveIndex) {
|
|
// is the XRef not saved yet?
|
|
switch (XRefDataBase[Oovpa->XRefSaveIndex]) {
|
|
case XREF_ADDR_NOT_FOUND:
|
|
{
|
|
EmuWarning("Found OOVPA after first finding nothing?");
|
|
// fallthrough to XREF_ADDR_UNDETERMINED
|
|
}
|
|
case XREF_ADDR_UNDETERMINED:
|
|
{
|
|
// save and count the found address
|
|
UnResolvedXRefs--;
|
|
XRefDataBase[Oovpa->XRefSaveIndex] = pFunc;
|
|
break;
|
|
}
|
|
case XREF_ADDR_DERIVE:
|
|
{
|
|
EmuWarning("Cannot derive a save index!");
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if (XRefDataBase[OovpaTable->Oovpa->XRefSaveIndex] != pFunc) {
|
|
EmuWarning("Found OOVPA on other address than in XRefDataBase!");
|
|
EmuWarning("%s: %4d - pFunc: %08X, stored: %08X", OovpaTable->szFuncName, Oovpa->XRefSaveIndex, pFunc, XRefDataBase[Oovpa->XRefSaveIndex]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Retrieve the associated patch, if any is available
|
|
void* addr = GetEmuPatchAddr(std::string(OovpaTable->szFuncName));
|
|
|
|
if (addr != nullptr) {
|
|
EmuInstallPatch(OovpaTable->szFuncName, pFunc, addr);
|
|
output << "\t*PATCHED*";
|
|
} else {
|
|
const char* checkDisableStr = nullptr;
|
|
size_t getFuncStrLength = strlen(OovpaTable->szFuncName);
|
|
|
|
if (getFuncStrLength > 10) {
|
|
checkDisableStr = &OovpaTable->szFuncName[getFuncStrLength - 10];
|
|
}
|
|
|
|
if (checkDisableStr != nullptr && strcmp(checkDisableStr, "_UNPATCHED") == 0) {
|
|
output << "\t*UNPATCHED*";
|
|
|
|
// Mention there's no patch available, if it was to be applied
|
|
} else if (!IsXRef) {
|
|
output << "\t*NO PATCH AVAILABLE!*";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// NOTE: Alternate fix, however it will not register symbols just like the original method did.
|
|
// We need to create an array for symbol, patch, library type, etc structure.
|
|
// Then we can replace checks below into permanent solution.
|
|
if (bLLE_APU && ((library_flag & XbSymbolLib_XACTENG) || (library_flag & XbSymbolLib_DSOUND) > 0)) {
|
|
// Do nothing if emulating LLE APU
|
|
} else if (bLLE_GPU && ((library_flag & XbSymbolLib_XGRAPHC) || (library_flag & XbSymbolLib_D3D8) || (library_flag & XbSymbolLib_D3D8LTCG) > 0)) {
|
|
// Do nothing if emulating LLE GPU
|
|
} else if (bLLE_USB && ((std::strcmp(symbol_str, "XInitDevices") == 0) || (std::strcmp(symbol_str, "XGetDevices") == 0) || (std::strcmp(symbol_str, "XGetDeviceChanges") == 0) ||
|
|
(std::strcmp(symbol_str, "XInputOpen") == 0) || (std::strcmp(symbol_str, "XInputClose") == 0) || (std::strcmp(symbol_str, "XInputPoll") == 0) ||
|
|
(std::strcmp(symbol_str, "XInputGetCapabilities") == 0) || (std::strcmp(symbol_str, "XInputGetState") == 0) || (std::strcmp(symbol_str, "XInputSetState") == 0) ||
|
|
(std::strcmp(symbol_str, "XGetDeviceEnumerationStatus") == 0) || (std::strcmp(symbol_str, "XInputGetDeviceDescription") == 0) || (std::strcmp(symbol_str, "XID_fCloseDevice") == 0))) {
|
|
// Do nothing for the xinput functions if emulating LLE USB
|
|
} else {
|
|
// Or else check if patch exist then patch it.
|
|
|
|
// Now that we found the address, store it (regardless if we patch it or not)
|
|
g_SymbolAddresses[symbol_str] = func_addr;
|
|
|
|
// Retrieve the associated patch, if any is available
|
|
void* addr = GetEmuPatchAddr(symbol_str);
|
|
|
|
if (addr != nullptr) {
|
|
EmuInstallPatch(symbol_str, func_addr, addr);
|
|
output << "\t*PATCHED*";
|
|
}
|
|
}
|
|
output << "\n";
|
|
printf(output.str().c_str());
|
|
}
|
|
|
|
// TODO: Move this into a function rather than duplicating from HLE scanning code
|
|
void EmuD3D_Init_DeferredStates()
|
|
{
|
|
if (g_SymbolAddresses.find("D3DDeferredRenderState") != g_SymbolAddresses.end()) {
|
|
XTL::EmuD3DDeferredRenderState = (DWORD*)g_SymbolAddresses["D3DDeferredRenderState"];
|
|
}
|
|
if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) {
|
|
XTL::EmuD3DDeferredTextureState = (DWORD*)g_SymbolAddresses["D3DDeferredTextureState"];
|
|
}
|
|
|
|
if (XTL::EmuD3DDeferredRenderState != nullptr) {
|
|
for (int v = 0; v < 44; v++) {
|
|
XTL::EmuD3DDeferredRenderState[v] = XTL::X_D3DRS_UNK;
|
|
}
|
|
}
|
|
if (XTL::EmuD3DDeferredTextureState != nullptr) {
|
|
for (int s = 0; s < 4; s++) {
|
|
for (int v = 0; v < 32; v++)
|
|
XTL::EmuD3DDeferredTextureState[v + s * 32] = X_D3DTSS_UNK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update shared structure with GUI process
|
|
void EmuUpdateLLEStatus(uint32_t XbLibScan)
|
|
{
|
|
uint FlagsLLE;
|
|
g_EmuShared->GetFlagsLLE(&FlagsLLE);
|
|
|
|
if ((FlagsLLE & LLE_GPU) == false
|
|
&& !((XbLibScan & XbSymbolLib_D3D8) > 0
|
|
|| (XbLibScan & XbSymbolLib_D3D8LTCG) > 0)) {
|
|
bLLE_GPU = true;
|
|
FlagsLLE ^= LLE_GPU;
|
|
EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE GPU.");
|
|
}
|
|
|
|
if ((FlagsLLE & LLE_APU) == false
|
|
&& (XbLibScan & XbSymbolLib_DSOUND) == 0) {
|
|
bLLE_APU = true;
|
|
FlagsLLE ^= LLE_APU;
|
|
EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE APU.");
|
|
}
|
|
#if 0 // Reenable this when LLE USB actually works
|
|
if ((FlagsLLE & LLE_USB) == false
|
|
&& (XbLibScan & XbSymbolLib_XAPILIB) == 0) {
|
|
bLLE_USB = true;
|
|
FlagsLLE ^= LLE_USB;
|
|
EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE USB.");
|
|
}
|
|
#endif
|
|
g_EmuShared->SetFlagsLLEStatus(FlagsLLE);
|
|
}
|
|
|
|
// NOTE: EmuHLEIntercept do not get to be in XbSymbolDatabase, do the intecept in Cxbx project only.
|
|
void EmuHLEIntercept(Xbe::Header *pXbeHeader)
|
|
{
|
|
// NOTE: Increase this revision number any time we changed something inside Cxbx-Reloaded.
|
|
int revisionCache = 5;
|
|
|
|
Xbe::LibraryVersion *pLibraryVersion = (Xbe::LibraryVersion*)pXbeHeader->dwLibraryVersionsAddr;
|
|
|
|
uint16 xdkVersion = 0;
|
|
uint32_t XbLibScan = 0;
|
|
|
|
// NOTE: We need to check if title has library header to optimize verification process.
|
|
if (pLibraryVersion != nullptr) {
|
|
uint32 dwLibraryVersions = pXbeHeader->dwLibraryVersions;
|
|
const char* SectionName = nullptr;
|
|
Xbe::SectionHeader* pSectionHeaders = (Xbe::SectionHeader*)pXbeHeader->dwSectionHeadersAddr;
|
|
|
|
// Get the highest revision build and prefix library to scan.
|
|
for (uint32 v = 0; v < dwLibraryVersions; v++) {
|
|
uint16 BuildVersion = pLibraryVersion[v].wBuildVersion;
|
|
uint16 QFEVersion = pLibraryVersion[v].wFlags.QFEVersion;
|
|
|
|
if (xdkVersion < BuildVersion) {
|
|
xdkVersion = BuildVersion;
|
|
}
|
|
XbLibScan |= XbSymbolLibrayToFlag(std::string(pLibraryVersion[v].szName, pLibraryVersion[v].szName + 8).c_str());
|
|
}
|
|
|
|
// Since XDK 4039 title does not have library version for DSOUND, let's check section header if it exists or not.
|
|
for (unsigned int v = 0; v < pXbeHeader->dwSections; v++) {
|
|
SectionName = (const char*)pSectionHeaders[v].dwSectionNameAddr;
|
|
if (strncmp(SectionName, Lib_DSOUND, 8) == 0) {
|
|
XbLibScan |= XbSymbolLib_DSOUND;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
EmuUpdateLLEStatus(XbLibScan);
|
|
uint gFlagsLLE;
|
|
g_EmuShared->GetFlagsLLEStatus(&gFlagsLLE);
|
|
|
|
printf("\n");
|
|
printf("*******************************************************************************\n");
|
|
printf("* Cxbx-Reloaded High Level Emulation database\n");
|
|
printf("*******************************************************************************\n");
|
|
printf("\n");
|
|
|
|
// Make sure the HLE Cache directory exists
|
|
std::string cachePath = std::string(szFolder_CxbxReloadedData) + "\\HLECache\\";
|
|
int result = SHCreateDirectoryEx(nullptr, cachePath.c_str(), nullptr);
|
|
if ((result != ERROR_SUCCESS) && (result != ERROR_ALREADY_EXISTS)) {
|
|
CxbxKrnlCleanup("Couldn't create Cxbx-Reloaded HLECache folder!");
|
|
}
|
|
|
|
// Hash the loaded XBE's header, use it as a filename
|
|
uint32_t uiHash = XXHash32::hash((void*)&CxbxKrnl_Xbe->m_Header, sizeof(Xbe::Header), 0);
|
|
std::stringstream sstream;
|
|
char tAsciiTitle[40] = "Unknown";
|
|
setlocale(LC_ALL, "English");
|
|
wcstombs(tAsciiTitle, g_pCertificate->wszTitleName, sizeof(tAsciiTitle));
|
|
std::string szTitleName(tAsciiTitle);
|
|
CxbxKrnl_Xbe->PurgeBadChar(szTitleName);
|
|
sstream << cachePath << szTitleName << "-" << std::hex << uiHash << ".ini";
|
|
std::string filename = sstream.str();
|
|
|
|
// This will fire when we exit this function scope; either after detecting a previous cache file, or when one is created
|
|
CxbxDebuggerScopedMessage hleCacheFilename(filename);
|
|
|
|
if (PathFileExists(filename.c_str())) {
|
|
printf("Found HLE Cache File: %08X.ini\n", uiHash);
|
|
|
|
g_BuildVersion = GetPrivateProfileInt("Libs", "D3D8_BuildVersion", 0, filename.c_str());
|
|
|
|
// Verify the version of the cache file against the HLE Database
|
|
const uint32 HLECacheHash = GetPrivateProfileInt("Info", "HLECacheHash", 0, filename.c_str());
|
|
|
|
if (HLECacheHash == XbSymbolLibraryVersion()) {
|
|
char buffer[SHRT_MAX] = { 0 };
|
|
char* bufferPtr = buffer;
|
|
g_HLECacheUsed = true;
|
|
|
|
const uint32 cacheRevision = GetPrivateProfileInt("Info", "revision", 0, filename.c_str());
|
|
const uint32 cacheFlagsLLE = GetPrivateProfileInt("Info", "FlagsLLE", 0, filename.c_str());
|
|
|
|
if (cacheFlagsLLE != gFlagsLLE) {
|
|
g_HLECacheUsed = false;
|
|
}
|
|
else if (cacheRevision != revisionCache) {
|
|
g_HLECacheUsed = false;
|
|
}
|
|
|
|
if (g_HLECacheUsed) {
|
|
printf("Using HLE Cache\n");
|
|
|
|
GetPrivateProfileSection("Symbols", buffer, sizeof(buffer), filename.c_str());
|
|
|
|
// Parse the .INI file into the map of symbol addresses
|
|
while (strlen(bufferPtr) > 0) {
|
|
std::string ini_entry(bufferPtr);
|
|
|
|
auto separator = ini_entry.find('=');
|
|
std::string key = ini_entry.substr(0, separator);
|
|
std::string value = ini_entry.substr(separator + 1, std::string::npos);
|
|
uint32_t addr = strtol(value.c_str(), 0, 16);
|
|
|
|
g_SymbolAddresses[key] = addr;
|
|
bufferPtr += strlen(bufferPtr) + 1;
|
|
}
|
|
|
|
// Iterate through the map of symbol addresses, calling GetEmuPatchAddr on all functions.
|
|
for (auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) {
|
|
std::string functionName = (*it).first;
|
|
xbaddr location = (*it).second;
|
|
|
|
std::stringstream output;
|
|
output << "HLECache: 0x" << std::setfill('0') << std::setw(8) << std::hex << location
|
|
<< " -> " << functionName;
|
|
void* pFunc = GetEmuPatchAddr(functionName);
|
|
if (pFunc != nullptr) {
|
|
// skip entries that weren't located at all
|
|
if (location == NULL) {
|
|
output << "\t(not patched)";
|
|
}
|
|
// Prevent patching illegal addresses
|
|
else if (location < XBE_IMAGE_BASE) {
|
|
output << "\t*ADDRESS TOO LOW!*";
|
|
} else if (location > g_SystemMaxMemory) {
|
|
output << "\t*ADDRESS TOO HIGH!*";
|
|
} else {
|
|
EmuInstallPatch(functionName, location, pFunc);
|
|
output << "\t*PATCHED*";
|
|
}
|
|
} else {
|
|
if (location != NULL)
|
|
output << "\t(no patch)";
|
|
}
|
|
|
|
output << "\n";
|
|
printf(output.str().c_str());
|
|
}
|
|
|
|
// Fix up Render state and Texture States
|
|
if (g_SymbolAddresses.find("D3DDeferredRenderState") == g_SymbolAddresses.end()
|
|
|| g_SymbolAddresses["D3DDeferredRenderState"] == 0) {
|
|
EmuWarning("EmuD3DDeferredRenderState was not found!");
|
|
}
|
|
|
|
if (g_SymbolAddresses.find("D3DDeferredTextureState") == g_SymbolAddresses.end()
|
|
|| g_SymbolAddresses["D3DDeferredTextureState"] == 0) {
|
|
EmuWarning("EmuD3DDeferredTextureState was not found!");
|
|
}
|
|
|
|
if (g_SymbolAddresses.find("D3DDEVICE") == g_SymbolAddresses.end()
|
|
|| g_SymbolAddresses["D3DDEVICE"] == 0) {
|
|
EmuWarning("D3DDEVICE was not found!");
|
|
}
|
|
|
|
EmuD3D_Init_DeferredStates();
|
|
}
|
|
}
|
|
|
|
// If g_SymbolAddresses didn't get filled, the HLE cache is invalid
|
|
if (g_SymbolAddresses.empty()) {
|
|
printf("HLE Cache file is outdated and will be regenerated\n");
|
|
g_HLECacheUsed = false;
|
|
}
|
|
}
|
|
|
|
// If the HLE Cache was used, skip symbol searching/patching
|
|
if (g_HLECacheUsed) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// initialize Microsoft XDK emulation
|
|
//
|
|
if(pLibraryVersion != nullptr) {
|
|
|
|
printf("HLE: Detected Microsoft XDK application...\n");
|
|
|
|
// TODO: Is this enough for alias? We need to verify it.
|
|
if ((XbLibScan & XbSymbolLib_D3D8) > 0 || (XbLibScan & XbSymbolLib_D3D8LTCG) > 0) {
|
|
g_BuildVersion = xdkVersion;
|
|
}
|
|
#if 0 // NOTE: This is a note for what we should do for above.
|
|
if (BuildVersion >= 5558 && BuildVersion <= 5659 && QFEVersion > 1) {
|
|
EmuWarning("D3D8 version 1.0.%d.%d Title Detected: This game uses an alias version 1.0.5788", BuildVersion, QFEVersion);
|
|
BuildVersion = 5788;
|
|
}
|
|
#endif
|
|
|
|
#if 0 // NOTE: This code is currently disabled due to not optimized and require more work to do.
|
|
|
|
XbSymbolRegisterLibrary(XbLibScan);
|
|
|
|
while (true) {
|
|
|
|
size_t SymbolSize = g_SymbolAddresses.size();
|
|
|
|
Xbe::SectionHeader* pSectionHeaders = reinterpret_cast<Xbe::SectionHeader*>(pXbeHeader->dwSectionHeadersAddr);
|
|
Xbe::SectionHeader* pSectionScan = nullptr;
|
|
|
|
for (uint32 v = 0; v < pXbeHeader->dwSections; v++) {
|
|
|
|
pSectionScan = pSectionHeaders + v;
|
|
|
|
XbSymbolScanSection((uint32_t)pXbeHeader, 64 * ONE_MB, (const char*)pSectionScan->dwSectionNameAddr, pSectionScan->dwVirtualAddr, pSectionScan->dwSizeofRaw, xdkVersion, EmuRegisterSymbol);
|
|
}
|
|
|
|
// If symbols are not adding to array, break the loop.
|
|
if (SymbolSize == g_SymbolAddresses.size()) {
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
XbSymbolSetOutputMessage(EmuOutputMessage);
|
|
|
|
XbSymbolScan(pXbeHeader, EmuRegisterSymbol);
|
|
|
|
EmuD3D_Init_DeferredStates();
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
// Write the HLE Database version string
|
|
{
|
|
std::string HLECacheHashString = std::to_string(XbSymbolLibraryVersion());
|
|
WritePrivateProfileString("Info", "HLECacheHash", HLECacheHashString.c_str(), filename.c_str());
|
|
}
|
|
|
|
|
|
std::stringstream revision;
|
|
revision << std::dec << revisionCache;
|
|
WritePrivateProfileString("Info", "revision", revision.str().c_str(), filename.c_str());
|
|
|
|
std::stringstream flagsLLE;
|
|
flagsLLE << std::dec << gFlagsLLE;
|
|
WritePrivateProfileString("Info", "FlagsLLE", flagsLLE.str().c_str(), filename.c_str());
|
|
|
|
// Write the Certificate Details to the cache file
|
|
WritePrivateProfileString("Certificate", "Name", tAsciiTitle, filename.c_str());
|
|
WritePrivateProfileString("Certificate", "TitleID", FormatTitleId(g_pCertificate->dwTitleId).c_str(), filename.c_str());
|
|
|
|
std::stringstream titleIdHex;
|
|
titleIdHex << std::hex << g_pCertificate->dwTitleId;
|
|
WritePrivateProfileString("Certificate", "TitleIDHex", titleIdHex.str().c_str(), filename.c_str());
|
|
|
|
std::stringstream region;
|
|
region << std::hex << g_pCertificate->dwGameRegion;
|
|
WritePrivateProfileString("Certificate", "Region", region.str().c_str(), filename.c_str());
|
|
|
|
// Write Library Details
|
|
for (uint i = 0; i < pXbeHeader->dwLibraryVersions; i++) {
|
|
std::string LibraryName(pLibraryVersion[i].szName, pLibraryVersion[i].szName + 8);
|
|
std::stringstream buildVersion;
|
|
buildVersion << pLibraryVersion[i].wBuildVersion;
|
|
|
|
WritePrivateProfileString("Libs", LibraryName.c_str(), buildVersion.str().c_str(), filename.c_str());
|
|
}
|
|
|
|
std::stringstream buildVersion;
|
|
buildVersion << g_BuildVersion;
|
|
WritePrivateProfileString("Libs", "D3D8_BuildVersion", buildVersion.str().c_str(), filename.c_str());
|
|
|
|
// Write the found symbol addresses into the cache file
|
|
for(auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) {
|
|
std::stringstream cacheAddress;
|
|
cacheAddress << std::hex << (*it).second;
|
|
WritePrivateProfileString("Symbols", (*it).first.c_str(), cacheAddress.str().c_str(), filename.c_str());
|
|
}
|
|
}
|
|
|
|
// NOTE: EmuInstallPatch do not get to be in XbSymbolDatabase, do the patches in Cxbx project only.
|
|
inline void EmuInstallPatch(std::string FunctionName, xbaddr FunctionAddr, void *Patch)
|
|
{
|
|
g_FunctionHooks[FunctionName].Install((void*)(FunctionAddr), Patch);
|
|
}
|
|
|
|
#if 0 // TODO: Need to move this into XbSymbolDatabase for depth verification usage.
|
|
#ifdef _DEBUG_TRACE
|
|
|
|
struct HLEVerifyContext {
|
|
const HLEData *main_data;
|
|
OOVPA *oovpa, *against;
|
|
const HLEData *against_data;
|
|
uint32 main_index, against_index;
|
|
};
|
|
|
|
std::string HLEErrorString(const HLEData *data, uint16_t buildVersion, uint32 index)
|
|
{
|
|
std::string result =
|
|
"OOVPATable " + (std::string)(data->LibSec.library) + "_" + std::to_string(buildVersion)
|
|
+ "[" + std::to_string(index) + "] "
|
|
+ (std::string)(data->OovpaTable[index].szFuncName);
|
|
|
|
return result;
|
|
}
|
|
|
|
void HLEError(HLEVerifyContext *context, uint16_t buildVersion, char *format, ...)
|
|
{
|
|
std::string output = "HLE Error ";
|
|
if (context->main_data != nullptr) {
|
|
output += "in " + HLEErrorString(context->main_data, buildVersion, context->main_index);
|
|
}
|
|
|
|
if (context->against != nullptr && context->against_data != nullptr) {
|
|
output += ", comparing against " + HLEErrorString(context->against_data, buildVersion, context->against_index);
|
|
}
|
|
|
|
// format specific error message
|
|
char buffer[200];
|
|
va_list args;
|
|
va_start(args, format);
|
|
vsprintf(buffer, format, args);
|
|
va_end(args);
|
|
|
|
output += " : " + (std::string)buffer + (std::string)"\n";
|
|
printf(output.c_str());
|
|
}
|
|
|
|
void VerifyHLEDataBaseAgainst(HLEVerifyContext *context); // forward
|
|
|
|
void VerifyHLEOOVPA(HLEVerifyContext *context, uint16_t buildVersion, OOVPA *oovpa)
|
|
{
|
|
if (context->against == nullptr) {
|
|
// TODO : verify XRefSaveIndex and XRef's (how?)
|
|
|
|
// verify offsets are in increasing order
|
|
uint32 prev_offset;
|
|
uint08 dummy_value;
|
|
GetOovpaEntry(oovpa, oovpa->XRefCount, prev_offset, dummy_value);
|
|
for (int p = oovpa->XRefCount + 1; p < oovpa->Count; p++) {
|
|
uint32 curr_offset;
|
|
GetOovpaEntry(oovpa, p, curr_offset, dummy_value);
|
|
if (!(curr_offset > prev_offset)) {
|
|
HLEError(context, buildVersion, "Lovp[%d] : Offset (0x%x) must be larger then previous offset (0x%x)",
|
|
p, curr_offset, prev_offset);
|
|
}
|
|
}
|
|
|
|
// find duplicate OOVPA's across all other data-table-oovpa's
|
|
context->oovpa = oovpa;
|
|
context->against = oovpa;
|
|
VerifyHLEDataBaseAgainst(context);
|
|
context->against = nullptr; // reset scanning state
|
|
return;
|
|
}
|
|
|
|
// prevent checking an oovpa against itself
|
|
if (context->against == oovpa) {
|
|
return;
|
|
}
|
|
|
|
// compare {Offset, Value}-pairs between two OOVPA's
|
|
OOVPA *left = context->against, *right = oovpa;
|
|
int l = 0, r = 0;
|
|
uint32 left_offset, right_offset;
|
|
uint08 left_value, right_value;
|
|
GetOovpaEntry(left, l, left_offset, left_value);
|
|
GetOovpaEntry(right, r, right_offset, right_value);
|
|
int unique_offset_left = 0;
|
|
int unique_offset_right = 0;
|
|
int equal_offset_value = 0;
|
|
int equal_offset_different_value = 0;
|
|
while (true) {
|
|
bool left_next = true;
|
|
bool right_next = true;
|
|
|
|
if (left_offset < right_offset) {
|
|
unique_offset_left++;
|
|
right_next = false;
|
|
} else if (left_offset > right_offset) {
|
|
unique_offset_right++;
|
|
left_next = false;
|
|
} else if (left_value == right_value) {
|
|
equal_offset_value++;
|
|
} else {
|
|
equal_offset_different_value++;
|
|
}
|
|
|
|
// increment r before use (in left_next)
|
|
if (right_next) {
|
|
r++;
|
|
}
|
|
|
|
if (left_next) {
|
|
l++;
|
|
if (l >= left->Count) {
|
|
unique_offset_right += right->Count - r;
|
|
break;
|
|
}
|
|
|
|
GetOovpaEntry(left, l, left_offset, left_value);
|
|
}
|
|
|
|
if (right_next) {
|
|
if (r >= right->Count) {
|
|
unique_offset_left += left->Count - l;
|
|
break;
|
|
}
|
|
|
|
GetOovpaEntry(right, r, right_offset, right_value);
|
|
}
|
|
}
|
|
|
|
// no mismatching values on identical offsets?
|
|
if (equal_offset_different_value == 0) {
|
|
// enough matching OV-pairs?
|
|
if (equal_offset_value > 4) {
|
|
// no unique OV-pairs on either side?
|
|
if (unique_offset_left + unique_offset_right == 0) {
|
|
HLEError(context, buildVersion, "OOVPA's are identical",
|
|
unique_offset_left,
|
|
unique_offset_right);
|
|
} else {
|
|
// not too many new OV-pairs on the left side?
|
|
if (unique_offset_left < 6) {
|
|
// not too many new OV-parirs on the right side?
|
|
if (unique_offset_right < 6) {
|
|
HLEError(context, buildVersion, "OOVPA's are expanded (left +%d, right +%d)",
|
|
unique_offset_left,
|
|
unique_offset_right);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void VerifyHLEDataEntry(HLEVerifyContext *context, const OOVPATable *table, uint32 index)
|
|
{
|
|
if (context->against == nullptr) {
|
|
context->main_index = index;
|
|
} else {
|
|
context->against_index = index;
|
|
}
|
|
|
|
if (context->against == nullptr) {
|
|
const char* checkDisableStr = nullptr;
|
|
size_t getFuncStrLength = strlen(table[index].szFuncName);
|
|
|
|
if (getFuncStrLength > 10) {
|
|
checkDisableStr = &table[index].szFuncName[getFuncStrLength - 10];
|
|
}
|
|
|
|
if (checkDisableStr != nullptr && strcmp(checkDisableStr, "_UNPATCHED") == 0) {
|
|
if (GetEmuPatchAddr((std::string)table[index].szFuncName)) {
|
|
HLEError(context, table[index].Version, "OOVPA registration UNPATCHED while a patch exists!");
|
|
}
|
|
} else if (table[index].Oovpa->XRefSaveIndex != XRefNoSaveIndex) {
|
|
if (GetEmuPatchAddr((std::string)table[index].szFuncName)) {
|
|
HLEError(context, table[index].Version, "OOVPA registration XREF while a patch exists!");
|
|
}
|
|
}
|
|
}
|
|
|
|
// verify the OOVPA of this entry
|
|
if (table[index].Oovpa != nullptr) {
|
|
VerifyHLEOOVPA(context, table[index].Version, table[index].Oovpa);
|
|
}
|
|
}
|
|
|
|
void VerifyHLEData(HLEVerifyContext *context, const HLEData *data)
|
|
{
|
|
if (context->against == nullptr) {
|
|
context->main_data = data;
|
|
} else {
|
|
context->against_data = data;
|
|
}
|
|
|
|
// Don't check a database against itself :
|
|
if (context->main_data == context->against_data) {
|
|
return;
|
|
}
|
|
|
|
// verify each entry in this HLEData
|
|
for (uint32 e = 0; e < data->OovpaTableCount; e++) {
|
|
VerifyHLEDataEntry(context, data->OovpaTable, e);
|
|
}
|
|
}
|
|
|
|
void VerifyHLEDataBaseAgainst(HLEVerifyContext *context)
|
|
{
|
|
// verify all HLEData's
|
|
for (uint32 d = 0; d < HLEDataBaseCount; d++) {
|
|
VerifyHLEData(context, &HLEDataBase[d]);
|
|
}
|
|
}
|
|
|
|
void VerifyHLEDataBase()
|
|
{
|
|
HLEVerifyContext context = { 0 };
|
|
VerifyHLEDataBaseAgainst(&context);
|
|
}
|
|
#endif // _DEBUG_TRACE
|
|
#endif
|