mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 23:55:40 +00:00
Cheats: Support parsing built-in database
This commit is contained in:
parent
d1399fe6a3
commit
eaafd0a00c
|
@ -80,7 +80,7 @@ std::string JStringToString(JNIEnv* env, jstring str)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GrowableMemoryByteStream> ReadInputStreamToMemory(JNIEnv* env, jobject obj, u32 chunk_size/* = 65536*/)
|
std::unique_ptr<GrowableMemoryByteStream> ReadInputStreamToMemory(JNIEnv* env, jobject obj, u32 chunk_size /* = 65536*/)
|
||||||
{
|
{
|
||||||
std::unique_ptr<GrowableMemoryByteStream> bs = std::make_unique<GrowableMemoryByteStream>(nullptr, 0);
|
std::unique_ptr<GrowableMemoryByteStream> bs = std::make_unique<GrowableMemoryByteStream>(nullptr, 0);
|
||||||
u32 position = 0;
|
u32 position = 0;
|
||||||
|
@ -190,14 +190,15 @@ float AndroidHostInterface::GetFloatSettingValue(const char* section, const char
|
||||||
return m_settings_interface.GetFloatValue(section, key, default_value);
|
return m_settings_interface.GetFloatValue(section, key, default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ByteStream> AndroidHostInterface::OpenPackageFile(const char *path, u32 flags)
|
std::unique_ptr<ByteStream> AndroidHostInterface::OpenPackageFile(const char* path, u32 flags)
|
||||||
{
|
{
|
||||||
Log_DevPrintf("OpenPackageFile(%s, %x)", path, flags);
|
Log_DevPrintf("OpenPackageFile(%s, %x)", path, flags);
|
||||||
if (flags & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE))
|
if (flags & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||||
jobject stream = env->CallObjectMethod(m_java_object, s_AndroidHostInterface_method_openAssetStream, env->NewStringUTF(path));
|
jobject stream =
|
||||||
|
env->CallObjectMethod(m_java_object, s_AndroidHostInterface_method_openAssetStream, env->NewStringUTF(path));
|
||||||
if (!stream)
|
if (!stream)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Package file '%s' not found", path);
|
Log_ErrorPrintf("Package file '%s' not found", path);
|
||||||
|
@ -695,7 +696,8 @@ void AndroidHostInterface::SetVibration(bool enabled)
|
||||||
m_last_vibration_update_time = current_time;
|
m_last_vibration_update_time = current_time;
|
||||||
|
|
||||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||||
if (m_emulation_activity_object) {
|
if (m_emulation_activity_object)
|
||||||
|
{
|
||||||
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_setVibration,
|
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_setVibration,
|
||||||
static_cast<jboolean>(enabled));
|
static_cast<jboolean>(enabled));
|
||||||
}
|
}
|
||||||
|
@ -735,8 +737,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
// Create global reference so it doesn't get cleaned up.
|
// Create global reference so it doesn't get cleaned up.
|
||||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||||
if ((s_String_class = env->FindClass("java/lang/String")) == nullptr ||
|
if ((s_String_class = env->FindClass("java/lang/String")) == nullptr ||
|
||||||
(s_String_class = static_cast<jclass>(env->NewGlobalRef(s_String_class))) ==
|
(s_String_class = static_cast<jclass>(env->NewGlobalRef(s_String_class))) == nullptr ||
|
||||||
nullptr ||
|
|
||||||
(s_AndroidHostInterface_class = env->FindClass("com/github/stenzek/duckstation/AndroidHostInterface")) ==
|
(s_AndroidHostInterface_class = env->FindClass("com/github/stenzek/duckstation/AndroidHostInterface")) ==
|
||||||
nullptr ||
|
nullptr ||
|
||||||
(s_AndroidHostInterface_class = static_cast<jclass>(env->NewGlobalRef(s_AndroidHostInterface_class))) ==
|
(s_AndroidHostInterface_class = static_cast<jclass>(env->NewGlobalRef(s_AndroidHostInterface_class))) ==
|
||||||
|
@ -757,8 +758,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
env->GetMethodID(s_AndroidHostInterface_class, "reportError", "(Ljava/lang/String;)V")) == nullptr ||
|
env->GetMethodID(s_AndroidHostInterface_class, "reportError", "(Ljava/lang/String;)V")) == nullptr ||
|
||||||
(s_AndroidHostInterface_method_reportMessage =
|
(s_AndroidHostInterface_method_reportMessage =
|
||||||
env->GetMethodID(s_AndroidHostInterface_class, "reportMessage", "(Ljava/lang/String;)V")) == nullptr ||
|
env->GetMethodID(s_AndroidHostInterface_class, "reportMessage", "(Ljava/lang/String;)V")) == nullptr ||
|
||||||
(s_AndroidHostInterface_method_openAssetStream =
|
(s_AndroidHostInterface_method_openAssetStream = env->GetMethodID(
|
||||||
env->GetMethodID(s_AndroidHostInterface_class, "openAssetStream", "(Ljava/lang/String;)Ljava/io/InputStream;")) == nullptr ||
|
s_AndroidHostInterface_class, "openAssetStream", "(Ljava/lang/String;)Ljava/io/InputStream;")) == nullptr ||
|
||||||
(emulation_activity_class = env->FindClass("com/github/stenzek/duckstation/EmulationActivity")) == nullptr ||
|
(emulation_activity_class = env->FindClass("com/github/stenzek/duckstation/EmulationActivity")) == nullptr ||
|
||||||
(s_EmulationActivity_method_reportError =
|
(s_EmulationActivity_method_reportError =
|
||||||
env->GetMethodID(emulation_activity_class, "reportError", "(Ljava/lang/String;)V")) == nullptr ||
|
env->GetMethodID(emulation_activity_class, "reportError", "(Ljava/lang/String;)V")) == nullptr ||
|
||||||
|
@ -770,8 +771,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
env->GetMethodID(emulation_activity_class, "onEmulationStopped", "()V")) == nullptr ||
|
env->GetMethodID(emulation_activity_class, "onEmulationStopped", "()V")) == nullptr ||
|
||||||
(s_EmulationActivity_method_onGameTitleChanged =
|
(s_EmulationActivity_method_onGameTitleChanged =
|
||||||
env->GetMethodID(emulation_activity_class, "onGameTitleChanged", "(Ljava/lang/String;)V")) == nullptr ||
|
env->GetMethodID(emulation_activity_class, "onGameTitleChanged", "(Ljava/lang/String;)V")) == nullptr ||
|
||||||
(s_EmulationActivity_method_setVibration =
|
(s_EmulationActivity_method_setVibration = env->GetMethodID(emulation_activity_class, "setVibration", "(Z)V")) ==
|
||||||
env->GetMethodID(emulation_activity_class, "setVibration", "(Z)V")) == nullptr ||
|
nullptr ||
|
||||||
(s_PatchCode_constructor = env->GetMethodID(s_PatchCode_class, "<init>", "(ILjava/lang/String;Z)V")) == nullptr)
|
(s_PatchCode_constructor = env->GetMethodID(s_PatchCode_class, "<init>", "(ILjava/lang/String;Z)V")) == nullptr)
|
||||||
{
|
{
|
||||||
Log_ErrorPrint("AndroidHostInterface lookups failed");
|
Log_ErrorPrint("AndroidHostInterface lookups failed");
|
||||||
|
@ -1040,10 +1041,15 @@ DEFINE_JNI_ARGS_METHOD(jobject, AndroidHostInterface_getPatchCodeList, jobject o
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||||
if (!System::HasCheatList() && !g_settings.auto_load_cheats)
|
if (!System::HasCheatList())
|
||||||
{
|
{
|
||||||
// Hopefully this won't deadlock...
|
// Hopefully this won't deadlock...
|
||||||
hi->RunOnEmulationThread([hi]() { hi->LoadCheatListFromGameTitle(); }, true);
|
hi->RunOnEmulationThread(
|
||||||
|
[hi]() {
|
||||||
|
if (!hi->LoadCheatListFromGameTitle())
|
||||||
|
hi->LoadCheatListFromDatabase();
|
||||||
|
},
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!System::HasCheatList())
|
if (!System::HasCheatList())
|
||||||
|
@ -1171,7 +1177,8 @@ DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_setMediaPlaylistIndex, job
|
||||||
|
|
||||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||||
hi->RunOnEmulationThread([index, hi]() {
|
hi->RunOnEmulationThread([index, hi]() {
|
||||||
if (System::IsValid()) {
|
if (System::IsValid())
|
||||||
|
{
|
||||||
if (!System::SwitchMediaFromPlaylist(index))
|
if (!System::SwitchMediaFromPlaylist(index))
|
||||||
hi->AddOSDMessage("Disc switch failed. Please make sure the file exists.");
|
hi->AddOSDMessage("Disc switch failed. Please make sure the file exists.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "cheats.h"
|
#include "cheats.h"
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/byte_stream.h"
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string.h"
|
#include "common/string.h"
|
||||||
|
@ -505,6 +506,123 @@ bool CheatList::SaveToPCSXRFile(const char* filename)
|
||||||
return (std::ferror(fp.get()) == 0);
|
return (std::ferror(fp.get()) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CheatList::LoadFromPackage(const std::string& game_code)
|
||||||
|
{
|
||||||
|
std::unique_ptr<ByteStream> stream =
|
||||||
|
g_host_interface->OpenPackageFile("database/chtdb.txt", BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
||||||
|
if (!stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string db_string = FileSystem::ReadStreamToString(stream.get());
|
||||||
|
stream.reset();
|
||||||
|
if (db_string.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::istringstream iss(db_string);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(iss, line))
|
||||||
|
{
|
||||||
|
char* start = line.data();
|
||||||
|
while (*start != '\0' && std::isspace(*start))
|
||||||
|
start++;
|
||||||
|
|
||||||
|
// skip empty lines
|
||||||
|
if (*start == '\0' || *start == ';')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char* end = start + std::strlen(start) - 1;
|
||||||
|
while (end > start && std::isspace(*end))
|
||||||
|
{
|
||||||
|
*end = '\0';
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == end)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (start[0] != ':' || std::strcmp(&start[1], game_code.c_str()) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// game code match
|
||||||
|
CheatCode current_code;
|
||||||
|
while (std::getline(iss, line))
|
||||||
|
{
|
||||||
|
start = line.data();
|
||||||
|
while (*start != '\0' && std::isspace(*start))
|
||||||
|
start++;
|
||||||
|
|
||||||
|
// skip empty lines
|
||||||
|
if (*start == '\0' || *start == ';')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
end = start + std::strlen(start) - 1;
|
||||||
|
while (end > start && std::isspace(*end))
|
||||||
|
{
|
||||||
|
*end = '\0';
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == end)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (start[0] == ':')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (start[0] == '#')
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
|
||||||
|
if (current_code.Valid())
|
||||||
|
{
|
||||||
|
m_codes.push_back(std::move(current_code));
|
||||||
|
current_code = CheatCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// new code
|
||||||
|
char* slash = std::strrchr(start, '\\');
|
||||||
|
if (slash)
|
||||||
|
{
|
||||||
|
*slash = '\0';
|
||||||
|
current_code.group = start;
|
||||||
|
start = slash + 1;
|
||||||
|
}
|
||||||
|
if (current_code.group.empty())
|
||||||
|
current_code.group = "Ungrouped";
|
||||||
|
|
||||||
|
current_code.description = start;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!IsHexCharacter(*start) && start != end)
|
||||||
|
start++;
|
||||||
|
if (start == end)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char* end_ptr;
|
||||||
|
CheatCode::Instruction inst;
|
||||||
|
inst.first = static_cast<u32>(std::strtoul(start, &end_ptr, 16));
|
||||||
|
inst.second = 0;
|
||||||
|
if (end_ptr)
|
||||||
|
{
|
||||||
|
while (!IsHexCharacter(*end_ptr) && end_ptr != end)
|
||||||
|
end_ptr++;
|
||||||
|
if (end_ptr != end)
|
||||||
|
inst.second = static_cast<u32>(std::strtoul(end_ptr, nullptr, 16));
|
||||||
|
}
|
||||||
|
current_code.instructions.push_back(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_code.Valid())
|
||||||
|
m_codes.push_back(std::move(current_code));
|
||||||
|
|
||||||
|
Log_InfoPrintf("Loaded %zu codes from package for %s", m_codes.size(), game_code.c_str());
|
||||||
|
return !m_codes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_WarningPrintf("No codes found in package for %s", game_code.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
u32 CheatList::GetEnabledCodeCount() const
|
u32 CheatList::GetEnabledCodeCount() const
|
||||||
{
|
{
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
|
|
|
@ -129,6 +129,8 @@ public:
|
||||||
|
|
||||||
bool SaveToPCSXRFile(const char* filename);
|
bool SaveToPCSXRFile(const char* filename);
|
||||||
|
|
||||||
|
bool LoadFromPackage(const std::string& game_code);
|
||||||
|
|
||||||
void Apply();
|
void Apply();
|
||||||
|
|
||||||
void ApplyCode(u32 index);
|
void ApplyCode(u32 index);
|
||||||
|
|
|
@ -263,7 +263,15 @@ CheatList* CheatManagerDialog::getCheatList() const
|
||||||
|
|
||||||
CheatList* list = System::GetCheatList();
|
CheatList* list = System::GetCheatList();
|
||||||
if (!list)
|
if (!list)
|
||||||
|
{
|
||||||
QtHostInterface::GetInstance()->LoadCheatListFromGameTitle();
|
QtHostInterface::GetInstance()->LoadCheatListFromGameTitle();
|
||||||
|
list = System::GetCheatList();
|
||||||
|
}
|
||||||
|
if (!list)
|
||||||
|
{
|
||||||
|
QtHostInterface::GetInstance()->LoadCheatListFromDatabase();
|
||||||
|
list = System::GetCheatList();
|
||||||
|
}
|
||||||
if (!list)
|
if (!list)
|
||||||
{
|
{
|
||||||
QtHostInterface::GetInstance()->executeOnEmulationThread(
|
QtHostInterface::GetInstance()->executeOnEmulationThread(
|
||||||
|
|
|
@ -2436,6 +2436,20 @@ bool CommonHostInterface::LoadCheatListFromGameTitle()
|
||||||
return LoadCheatList(filename.c_str());
|
return LoadCheatList(filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CommonHostInterface::LoadCheatListFromDatabase()
|
||||||
|
{
|
||||||
|
if (System::GetRunningCode().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::unique_ptr<CheatList> cl = std::make_unique<CheatList>();
|
||||||
|
if (!cl->LoadFromPackage(System::GetRunningCode()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AddFormattedOSDMessage(10.0f, TranslateString("OSDMessage", "Loaded %u cheats from database."), cl->GetCodeCount());
|
||||||
|
System::SetCheatList(std::move(cl));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CommonHostInterface::SaveCheatList()
|
bool CommonHostInterface::SaveCheatList()
|
||||||
{
|
{
|
||||||
if (!System::IsValid() || !System::HasCheatList())
|
if (!System::IsValid() || !System::HasCheatList())
|
||||||
|
|
|
@ -160,6 +160,9 @@ public:
|
||||||
/// Loads the cheat list for the current game title from the user directory.
|
/// Loads the cheat list for the current game title from the user directory.
|
||||||
bool LoadCheatListFromGameTitle();
|
bool LoadCheatListFromGameTitle();
|
||||||
|
|
||||||
|
/// Loads the cheat list for the current game code from the built-in code database.
|
||||||
|
bool LoadCheatListFromDatabase();
|
||||||
|
|
||||||
/// Saves the current cheat list to the game title's file.
|
/// Saves the current cheat list to the game title's file.
|
||||||
bool SaveCheatList();
|
bool SaveCheatList();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue