mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-04 22:11:18 +12:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
402
src/Cafe/GameProfile/GameProfile.cpp
Normal file
402
src/Cafe/GameProfile/GameProfile.cpp
Normal file
|
@ -0,0 +1,402 @@
|
|||
#include "Cafe/GameProfile/GameProfile.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
|
||||
#include "boost/nowide/convert.hpp"
|
||||
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "Common/filestream.h"
|
||||
#include "util/IniParser/IniParser.h"
|
||||
#include "util/helpers/StringHelpers.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
|
||||
std::unique_ptr<GameProfile> g_current_game_profile = std::make_unique<GameProfile>();
|
||||
|
||||
struct gameProfileBooleanOption_t
|
||||
{
|
||||
bool isPresent = false;
|
||||
bool value;
|
||||
};
|
||||
|
||||
/*
|
||||
* Attempts to load a boolean option
|
||||
* If the option exists, true is returned.
|
||||
* The boolean is stored in *optionValue
|
||||
*/
|
||||
__declspec(dllexport) bool gameProfile_loadBooleanOption(IniParser* iniParser, char* optionName, gameProfileBooleanOption_t* option)
|
||||
{
|
||||
auto option_value = iniParser->FindOption(optionName);
|
||||
option->isPresent = false;
|
||||
option->value = false;
|
||||
if (!option_value)
|
||||
return false;
|
||||
// parse option
|
||||
if (boost::iequals(*option_value, "false") || boost::iequals(*option_value, "0"))
|
||||
{
|
||||
option->isPresent = true;
|
||||
option->value = false;
|
||||
return true;
|
||||
}
|
||||
else if (boost::iequals(*option_value, "true") || boost::iequals(*option_value, "1"))
|
||||
{
|
||||
option->isPresent = true;
|
||||
option->value = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
cemuLog_force("Unknown value '{}' for option '{}' in game profile", *option_value, optionName);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool gameProfile_loadBooleanOption2(IniParser& iniParser, const char* optionName, bool& option)
|
||||
{
|
||||
auto option_value = iniParser.FindOption(optionName);
|
||||
if (!option_value)
|
||||
return false;
|
||||
if (boost::iequals(*option_value, "1") || boost::iequals(*option_value, "true"))
|
||||
{
|
||||
option = true;
|
||||
return true;
|
||||
}
|
||||
else if (boost::iequals(*option_value, "0") || boost::iequals(*option_value, "false"))
|
||||
{
|
||||
option = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
cemuLog_force("Unknown value '{}' for option '{}' in game profile", *option_value, optionName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gameProfile_loadBooleanOption2(IniParser& iniParser, const char* optionName, std::optional<bool>& option)
|
||||
{
|
||||
bool tmp;
|
||||
const auto result = gameProfile_loadBooleanOption2(iniParser, optionName, tmp);
|
||||
if(result)
|
||||
option = tmp;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to load a integer option
|
||||
* Allows to specify min and max value (error is logged if out of range and default value is picked)
|
||||
*/
|
||||
__declspec(dllexport) bool gameProfile_loadIntegerOption(IniParser* iniParser, const char* optionName, gameProfileIntegerOption_t* option, sint32 defaultValue, sint32 minVal, sint32 maxVal)
|
||||
{
|
||||
auto option_value = iniParser->FindOption(optionName);
|
||||
option->isPresent = false;
|
||||
if (!option_value)
|
||||
{
|
||||
option->value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
// parse option
|
||||
sint32 val = StringHelpers::ToInt(*option_value, defaultValue);
|
||||
if (val < minVal || val > maxVal)
|
||||
{
|
||||
cemuLog_force("Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
|
||||
option->value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
option->isPresent = true;
|
||||
option->value = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool gameProfile_loadIntegerOption(IniParser& iniParser, const char* optionName, T& option, T minVal, T maxVal)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value);
|
||||
auto option_value = iniParser.FindOption(optionName);
|
||||
if (!option_value)
|
||||
return false;
|
||||
// parse option
|
||||
try
|
||||
{
|
||||
T val = ConvertString<T>(*option_value);
|
||||
if (val < minVal || val > maxVal)
|
||||
{
|
||||
cemuLog_force("Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
|
||||
return false;
|
||||
}
|
||||
|
||||
option = val;
|
||||
return true;
|
||||
}
|
||||
catch(std::exception&)
|
||||
{
|
||||
cemuLog_force("Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool gameProfile_loadEnumOption(IniParser& iniParser, const char* optionName, T& option)
|
||||
{
|
||||
static_assert(std::is_enum<T>::value);
|
||||
auto option_value = iniParser.FindOption(optionName);
|
||||
if (!option_value)
|
||||
return false;
|
||||
for(const T& v : T())
|
||||
{
|
||||
// test integer option
|
||||
if (boost::iequals(fmt::format("{}", static_cast<typename std::underlying_type<T>::type>(v)), *option_value))
|
||||
{
|
||||
option = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
// test enum name
|
||||
if(boost::iequals(fmt::format("{}", v), *option_value))
|
||||
{
|
||||
option = v;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool gameProfile_loadEnumOption(IniParser& iniParser, const char* optionName, std::optional<T>& option)
|
||||
{
|
||||
T tmp;
|
||||
const auto result = gameProfile_loadEnumOption(iniParser, optionName, tmp);
|
||||
if(result)
|
||||
option = tmp;
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma optimize( "", off )
|
||||
__declspec(dllexport) __declspec(noinline) void gameProfile_categoryBegin(IniParser* iniParser)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
#pragma optimize( "", on )
|
||||
|
||||
void gameProfile_load()
|
||||
{
|
||||
g_current_game_profile->ResetOptional(); // reset with global values as optional
|
||||
g_current_game_profile->Load(CafeSystem::GetForegroundTitleId(), true);
|
||||
|
||||
// apply some settings immediately
|
||||
ppcThreadQuantum = g_current_game_profile->GetThreadQuantum();
|
||||
|
||||
if (ppcThreadQuantum != GameProfile::kThreadQuantumDefault)
|
||||
cemuLog_force("Thread quantum set to {}", ppcThreadQuantum);
|
||||
}
|
||||
|
||||
bool GameProfile::Load(uint64_t title_id, bool notifyCemuhook)
|
||||
{
|
||||
auto gameProfilePath = ActiveSettings::GetPath("gameProfiles/{:016x}.ini", title_id);
|
||||
|
||||
std::optional<std::vector<uint8>> profileContents = FileStream::LoadIntoMemory(gameProfilePath);
|
||||
if (!profileContents)
|
||||
{
|
||||
gameProfilePath = ActiveSettings::GetPath("gameProfiles/default/{:016x}.ini", title_id);
|
||||
profileContents = FileStream::LoadIntoMemory(gameProfilePath);
|
||||
if (!profileContents)
|
||||
return false;
|
||||
m_is_default = true;
|
||||
}
|
||||
else
|
||||
m_is_default = false;
|
||||
|
||||
m_is_loaded = true;
|
||||
// most official gameprofiles start with "# gamename"
|
||||
std::vector<char> game_name;
|
||||
if (profileContents->size() > 0 && profileContents->data()[0] == '#')
|
||||
{
|
||||
char c;
|
||||
size_t idx = 1;
|
||||
while (idx < profileContents->size() && (c = profileContents->data()[idx]) != '\n' && idx < 128)
|
||||
{
|
||||
game_name.emplace_back(c);
|
||||
idx++;
|
||||
}
|
||||
m_gameName = std::string(game_name.begin(), game_name.end());
|
||||
trim(m_gameName.value());
|
||||
}
|
||||
IniParser iniParser(*profileContents, gameProfilePath.string());
|
||||
// parse ini
|
||||
while (iniParser.NextSection())
|
||||
{
|
||||
//if (notifyCemuhook)
|
||||
// gameProfile_categoryBegin(gameProfile); // hookable export for Cemuhook
|
||||
|
||||
if (boost::iequals(iniParser.GetCurrentSectionName(), "General"))
|
||||
{
|
||||
gameProfile_loadBooleanOption2(iniParser, "loadSharedLibraries", m_loadSharedLibraries);
|
||||
gameProfile_loadBooleanOption2(iniParser, "startWithPadView", m_startWithPadView);
|
||||
}
|
||||
else if (boost::iequals(iniParser.GetCurrentSectionName(), "Graphics"))
|
||||
{
|
||||
gameProfileIntegerOption_t graphicsApi;
|
||||
gameProfile_loadIntegerOption(&iniParser, "graphics_api", &graphicsApi, -1, 0, 1);
|
||||
if (graphicsApi.value != -1)
|
||||
m_graphics_api = (GraphicAPI)graphicsApi.value;
|
||||
|
||||
gameProfile_loadEnumOption(iniParser, "accurateShaderMul", m_accurateShaderMul);
|
||||
|
||||
// legacy support
|
||||
auto option_precompiledShaders = iniParser.FindOption("precompiledShaders");
|
||||
if (option_precompiledShaders)
|
||||
{
|
||||
if (boost::iequals(*option_precompiledShaders, "1") || boost::iequals(*option_precompiledShaders, "true"))
|
||||
m_precompiledShaders = PrecompiledShaderOption::Enable;
|
||||
else if (boost::iequals(*option_precompiledShaders, "0") || boost::iequals(*option_precompiledShaders, "false"))
|
||||
m_precompiledShaders = PrecompiledShaderOption::Disable;
|
||||
else
|
||||
m_precompiledShaders = PrecompiledShaderOption::Auto;
|
||||
}
|
||||
else
|
||||
m_precompiledShaders = PrecompiledShaderOption::Auto;
|
||||
}
|
||||
else if (boost::iequals(iniParser.GetCurrentSectionName(), "Audio"))
|
||||
{
|
||||
gameProfile_loadBooleanOption2(iniParser, "disableAudio", m_disableAudio);
|
||||
}
|
||||
else if (boost::iequals(iniParser.GetCurrentSectionName(), "CPU"))
|
||||
{
|
||||
gameProfile_loadIntegerOption(iniParser, "threadQuantum", m_threadQuantum, 1000U, 536870912U);
|
||||
if (!gameProfile_loadEnumOption(iniParser, "cpuMode", m_cpuMode))
|
||||
{
|
||||
// try to load the old enum value strings
|
||||
std::optional<CPUModeLegacy> cpu_mode_legacy;
|
||||
if (gameProfile_loadEnumOption(iniParser, "cpuMode", cpu_mode_legacy) && cpu_mode_legacy.has_value())
|
||||
{
|
||||
m_cpuMode = (CPUMode)cpu_mode_legacy.value();
|
||||
if (m_cpuMode == CPUMode::DualcoreRecompiler)
|
||||
m_cpuMode = CPUMode::MulticoreRecompiler;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (boost::iequals(iniParser.GetCurrentSectionName(), "Controller"))
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
auto option_value = iniParser.FindOption(fmt::format("controller{}", (i + 1)));
|
||||
if (option_value)
|
||||
m_controllerProfile[i] = std::string(*option_value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameProfile::Save(uint64_t title_id)
|
||||
{
|
||||
auto gameProfilePath = ActiveSettings::GetPath("gameProfiles/{:016x}.ini", title_id);
|
||||
FileStream* fs = FileStream::createFile2(gameProfilePath);
|
||||
if (!fs)
|
||||
{
|
||||
cemuLog_force("Failed to write game profile");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_gameName)
|
||||
fs->writeLine(fmt::format("# {}\n", m_gameName.value()).c_str());
|
||||
|
||||
#define WRITE_OPTIONAL_ENTRY(__NAME) if (m_##__NAME) fs->writeLine(fmt::format("{} = {}", #__NAME, m_##__NAME.value()).c_str());
|
||||
#define WRITE_ENTRY(__NAME) fs->writeLine(fmt::format("{} = {}", #__NAME, m_##__NAME).c_str());
|
||||
|
||||
fs->writeLine("[General]");
|
||||
WRITE_OPTIONAL_ENTRY(loadSharedLibraries);
|
||||
WRITE_ENTRY(startWithPadView);
|
||||
|
||||
fs->writeLine("");
|
||||
|
||||
|
||||
fs->writeLine("[CPU]");
|
||||
WRITE_OPTIONAL_ENTRY(cpuMode);
|
||||
WRITE_ENTRY(threadQuantum);
|
||||
|
||||
fs->writeLine("");
|
||||
|
||||
fs->writeLine("[Graphics]");
|
||||
//WRITE_OPTIONAL_ENTRY(gpuBufferCacheAccuracy);
|
||||
WRITE_ENTRY(accurateShaderMul);
|
||||
WRITE_OPTIONAL_ENTRY(precompiledShaders);
|
||||
WRITE_OPTIONAL_ENTRY(graphics_api);
|
||||
fs->writeLine("");
|
||||
|
||||
/*stream_writeLine(stream_gameProfile, "[Audio]");
|
||||
WRITE_ENTRY(disableAudio);
|
||||
stream_writeLine(stream_gameProfile, "");*/
|
||||
|
||||
fs->writeLine("[Controller]");
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (m_controllerProfile[i])
|
||||
fs->writeLine(fmt::format("controller{} = {}", (i + 1), m_controllerProfile[i].value()).c_str());
|
||||
}
|
||||
|
||||
fs->writeLine("");
|
||||
|
||||
#undef WRITE_OPTIONAL_ENTRY
|
||||
#undef WRITE_ENTRY
|
||||
|
||||
delete fs;
|
||||
}
|
||||
|
||||
void GameProfile::ResetOptional()
|
||||
{
|
||||
m_gameName.reset();
|
||||
|
||||
// general settings
|
||||
m_loadSharedLibraries.reset(); // true;
|
||||
m_startWithPadView = false;
|
||||
|
||||
// graphic settings
|
||||
m_accurateShaderMul = AccurateShaderMulOption::True;
|
||||
// cpu settings
|
||||
m_threadQuantum = kThreadQuantumDefault;
|
||||
m_cpuMode.reset(); // CPUModeOption::kSingleCoreRecompiler;
|
||||
// audio
|
||||
m_disableAudio = false;
|
||||
// controller settings
|
||||
for (auto& profile : m_controllerProfile)
|
||||
profile.reset();
|
||||
}
|
||||
|
||||
void GameProfile::Reset()
|
||||
{
|
||||
m_gameName.reset();
|
||||
|
||||
// general settings
|
||||
m_loadSharedLibraries = true;
|
||||
m_startWithPadView = false;
|
||||
|
||||
// graphic settings
|
||||
m_accurateShaderMul = AccurateShaderMulOption::True;
|
||||
m_precompiledShaders = PrecompiledShaderOption::Auto;
|
||||
// cpu settings
|
||||
m_threadQuantum = kThreadQuantumDefault;
|
||||
m_cpuMode = CPUMode::Auto;
|
||||
// audio
|
||||
m_disableAudio = false;
|
||||
// controller settings
|
||||
for (auto& profile : m_controllerProfile)
|
||||
profile.reset();
|
||||
}
|
||||
|
||||
// legacy code for Cemuhook
|
||||
__declspec(dllexport) char* gameProfile_loadStringOption(IniParser* iniParser, char* optionName)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
__declspec(dllexport) char* gameProfile_getCurrentCategoryName(IniParser* iniParser)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct gpNamedOptionEntry_t
|
||||
{
|
||||
char* name;
|
||||
sint32 value;
|
||||
};
|
||||
|
||||
__declspec(dllexport) bool gameProfile_loadIntegerNamedOption(IniParser* iniParser, char* optionName, gameProfileIntegerOption_t* option, sint32 defaultValue, const gpNamedOptionEntry_t* nameValues, sint32 numNameValues)
|
||||
{
|
||||
return false;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue