diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index 886b0acbb1..b50ed2ce8d 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -22,8 +22,6 @@ #endif static int CheatEvent = -1; -std::string gameTitle; -std::string activeCheatFile; static CWCheatEngine *cheatEngine; static bool cheatsEnabled; void hleCheat(u64 userdata, int cyclesLate); @@ -43,9 +41,9 @@ class CheatFileParser { public: CheatFileParser(const std::string &filename, const std::string &gameID = "") { #if defined(_WIN32) && !defined(__MINGW32__) - file_.open(ConvertUTF8ToWString(activeCheatFile)); + file_.open(ConvertUTF8ToWString(filename)); #else - file_.open(activeCheatFile.c_str()); + file_.open(filename.c_str()); #endif validGameID_ = ReplaceAll(gameID, "-", ""); @@ -61,8 +59,13 @@ public: return cheats_; } + std::vector GetFileInfo() const { + return cheatInfo_; + } + protected: void Flush(); + void FlushCheatInfo(); void AddError(const std::string &msg); void ParseLine(const std::string &line); void ParseDataLine(const std::string &line, CheatCodeFormat format); @@ -74,9 +77,11 @@ protected: int line_ = 0; int games_ = 0; std::vector errors_; + std::vector cheatInfo_; std::vector cheats_; std::vector pendingLines_; CheatCodeFormat codeFormat_ = CheatCodeFormat::UNDEFINED; + CheatFileInfo lastCheatInfo_; bool gameEnabled_ = true; bool gameRiskyEnabled_ = false; bool cheatEnabled_ = false; @@ -88,9 +93,7 @@ bool CheatFileParser::Parse() { getline(file_, line, '\n'); line = TrimString(line); - // Minimum length is set to 5 just to match GetCodesList() function - // which discards anything shorter when called anyway. - // It's decided from shortest possible _ lines name of the game "_G N+" + // Minimum length 5 is shortest possible _ lines name of the game "_G N+" // and a minimum of 1 displayable character in cheat name string "_C0 1" // which both equal to 5 characters. if (line.length() >= 5 && line[0] == '_') { @@ -111,12 +114,20 @@ bool CheatFileParser::Parse() { void CheatFileParser::Flush() { if (!pendingLines_.empty()) { + FlushCheatInfo(); cheats_.push_back({ codeFormat_, pendingLines_ }); pendingLines_.clear(); } codeFormat_ = CheatCodeFormat::UNDEFINED; } +void CheatFileParser::FlushCheatInfo() { + if (lastCheatInfo_.lineNum != 0) { + cheatInfo_.push_back(lastCheatInfo_); + lastCheatInfo_ = { 0 }; + } +} + void CheatFileParser::AddError(const std::string &err) { errors_.push_back(StringFromFormat("Error on line %d: %s", line_, err.c_str())); } @@ -132,6 +143,7 @@ void CheatFileParser::ParseLine(const std::string &line) { if (gameRiskyEnabled_) { // We found the right one, so let's not use this risky stuff. cheats_.clear(); + cheatInfo_.clear(); gameRiskyEnabled_ = false; } gameEnabled_ = true; @@ -144,6 +156,7 @@ void CheatFileParser::ParseLine(const std::string &line) { if (gameRiskyEnabled_) { // There are multiple games here, kill the risky stuff. cheats_.clear(); + cheatInfo_.clear(); gameRiskyEnabled_ = false; } gameEnabled_ = false; @@ -155,18 +168,20 @@ void CheatFileParser::ParseLine(const std::string &line) { return; case 'C': + Flush(); + // Cheat name and activation status. if (line.length() >= 3 && line[2] >= '1' && line[2] <= '9') { + lastCheatInfo_ = { line_, line.length() >= 5 ? line.substr(4) : "", true }; cheatEnabled_ = true; } else if (line.length() >= 3 && line[2] == '0') { + lastCheatInfo_ = { line_, line.length() >= 5 ? line.substr(4) : "", false }; cheatEnabled_ = false; } else { AddError("could not parse cheat name line"); cheatEnabled_ = false; return; } - - Flush(); return; case 'L': @@ -190,11 +205,16 @@ void CheatFileParser::ParseDataLine(const std::string &line, CheatCodeFormat for codeFormat_ = format; } else if (codeFormat_ != format) { AddError("mixed code format (cwcheat/tempar)"); + lastCheatInfo_ = { 0 }; pendingLines_.clear(); cheatEnabled_ = false; } - if (!cheatEnabled_ || !gameEnabled_) { + if (!gameEnabled_) { + return; + } + if (!cheatEnabled_) { + FlushCheatInfo(); return; } @@ -225,13 +245,20 @@ static void __CheatStop() { static void __CheatStart() { __CheatStop(); - gameTitle = g_paramSFO.GetValueString("DISC_ID"); + std::string realGameID = g_paramSFO.GetValueString("DISC_ID"); + std::string gameID = realGameID; + const std::string gamePath = PSP_CoreParameter().fileToStart; + const bool badGameSFO = realGameID.empty() || !g_paramSFO.GetValueInt("DISC_TOTAL"); + if (badGameSFO && gamePath.find("/PSP/GAME/") != std::string::npos) { + gameID = g_paramSFO.GenerateFakeID(gamePath); + } - if (gameTitle != "") { //this only generates ini files on boot, let's leave homebrew ini file for UI + cheatEngine = new CWCheatEngine(gameID); + // This only generates ini files on boot, let's leave homebrew ini file for UI. + if (!realGameID.empty()) { cheatEngine->CreateCheatFile(); } - cheatEngine = new CWCheatEngine(); cheatEngine->ParseCheats(); g_Config.bReloadCheats = false; cheatsEnabled = true; @@ -326,28 +353,32 @@ void hleCheat(u64 userdata, int cyclesLate) { cheatEngine->Run(); } -CWCheatEngine::CWCheatEngine() { +CWCheatEngine::CWCheatEngine(const std::string &gameID) : gameID_(gameID) { + filename_ = GetSysDirectory(DIRECTORY_CHEATS) + gameID_ + ".ini"; } void CWCheatEngine::CreateCheatFile() { - activeCheatFile = GetSysDirectory(DIRECTORY_CHEATS) + gameTitle + ".ini"; File::CreateFullPath(GetSysDirectory(DIRECTORY_CHEATS)); - if (!File::Exists(activeCheatFile)) { - FILE *f = File::OpenCFile(activeCheatFile, "wb"); + if (!File::Exists(filename_)) { + FILE *f = File::OpenCFile(filename_, "wb"); if (f) { fwrite("\xEF\xBB\xBF\n", 1, 4, f); fclose(f); } - if (!File::Exists(activeCheatFile)) { + if (!File::Exists(filename_)) { auto err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Unable to create cheat file, disk may be full")); } } } +std::string CWCheatEngine::CheatFilename() { + return filename_; +} + void CWCheatEngine::ParseCheats() { - CheatFileParser parser(activeCheatFile, gameTitle); + CheatFileParser parser(filename_, gameID_); parser.Parse(); // TODO: Report errors. @@ -361,37 +392,11 @@ u32 CWCheatEngine::GetAddress(u32 value) { return address; } -std::vector CWCheatEngine::GetCodesList() { - // Reads the entire cheat list from the appropriate .ini. - std::vector codesList; -#if defined(_WIN32) && !defined(__MINGW32__) - std::ifstream list(ConvertUTF8ToWString(activeCheatFile)); -#else - std::ifstream list(activeCheatFile.c_str()); -#endif - while (list && !list.eof()) { - std::string line; - getline(list, line, '\n'); +std::vector CWCheatEngine::FileInfo() { + CheatFileParser parser(filename_, gameID_); - bool validCheatLine = false; - // This function is called by cheat menu(UI) which doesn't support empty names - // minimum 1 non space character is required starting from 5 position. - // It also goes through other "_" lines, but they all have to meet this requirement anyway - // so we don't have to specify any syntax checks here that are made by cheat engine. - if (line.length() >= 5 && line[0] == '_') { - for (size_t i = 4; i < line.length(); i++) { - if (line[i] != ' ') { - validCheatLine = true; - break; - } - } - } - // Any lines not passing this check are discarded when we save changes to the cheat ini file - if (validCheatLine || (line.length() >= 2 && line[0] == '/' && line[1] == '/') || (line.length() >= 1 && line[0] == '#')) { - codesList.push_back(TrimString(line)); - } - } - return codesList; + parser.Parse(); + return parser.GetFileInfo(); } void CWCheatEngine::InvalidateICache(u32 addr, int size) { diff --git a/Core/CwCheat.h b/Core/CwCheat.h index 882093db3d..00c6134fd9 100644 --- a/Core/CwCheat.h +++ b/Core/CwCheat.h @@ -1,6 +1,8 @@ // Rough and ready CwCheats implementation, disabled by default. // Will not enable by default until the TOOD:s have been addressed. +#pragma once + #include #include #include @@ -34,14 +36,21 @@ struct CheatCode { std::vector lines; }; +struct CheatFileInfo { + int lineNum; + std::string name; + bool enabled; +}; + struct CheatOperation; class CWCheatEngine { public: - CWCheatEngine(); - std::vector GetCodesList(); + CWCheatEngine(const std::string &gameID); + std::vector FileInfo(); void ParseCheats(); void CreateCheatFile(); + std::string CheatFilename(); void Run(); bool HasCheats(); @@ -59,4 +68,6 @@ private: bool TestIfAddr(const CheatOperation &op, bool(*oper)(int a, int b)); std::vector cheats_; + std::string gameID_; + std::string filename_; }; diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 2ab0a47a0a..c36056b273 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -15,12 +15,10 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include - -#include "input/input_state.h" +#include "ext/cityhash/city.h" +#include "i18n/i18n.h" #include "ui/ui.h" #include "util/text/utf8.h" -#include "i18n/i18n.h" #include "Common/FileUtil.h" #include "Core/Core.h" @@ -28,67 +26,63 @@ #include "Core/CwCheat.h" #include "Core/MIPS/JitCommon/JitCommon.h" -#include "UI/OnScreenDisplay.h" - -#include "UI/MainScreen.h" -#include "UI/EmuScreen.h" #include "UI/GameInfoCache.h" -#include "UI/MiscScreens.h" #include "UI/CwCheatScreen.h" -static bool enableAll = false; -static std::vector cheatList; -static CWCheatEngine *cheatEngine2; -static std::deque bEnableCheat; -static std::string gamePath_; +static const int FILE_CHECK_FRAME_INTERVAL = 53; - -CwCheatScreen::CwCheatScreen(std::string gamePath) +CwCheatScreen::CwCheatScreen(const std::string &gamePath) : UIDialogScreenWithBackground() { gamePath_ = gamePath; } -void CwCheatScreen::CreateCodeList() { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0); +CwCheatScreen::~CwCheatScreen() { + delete engine_; +} + +void CwCheatScreen::LoadCheatInfo() { + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); + std::string gameID; if (info && info->paramSFOLoaded) { - gameTitle = info->paramSFO.GetValueString("DISC_ID"); + gameID = info->paramSFO.GetValueString("DISC_ID"); } if ((info->id.empty() || !info->disc_total) && gamePath_.find("/PSP/GAME/") != std::string::npos) { - gameTitle = g_paramSFO.GenerateFakeID(gamePath_); + gameID = g_paramSFO.GenerateFakeID(gamePath_); } - cheatEngine2 = new CWCheatEngine(); - cheatEngine2->CreateCheatFile(); - cheatList = cheatEngine2->GetCodesList(); - - bEnableCheat.clear(); - formattedList_.clear(); - for (size_t i = 0; i < cheatList.size(); i++) { - if (cheatList[i][0] == '_' && cheatList[i][1] == 'C') { - formattedList_.push_back(cheatList[i].substr(4)); - if (cheatList[i][2] == '0') { - bEnableCheat.push_back(false); - } else { - bEnableCheat.push_back(true); - } - } + if (engine_ == nullptr || gameID != gameID_) { + gameID_ = gameID; + delete engine_; + engine_ = new CWCheatEngine(gameID_); + engine_->CreateCheatFile(); } - delete cheatEngine2; + + // We won't parse this, just using it to detect changes to the file. + std::string str; + if (readFileToString(true, engine_->CheatFilename().c_str(), str)) { + fileCheckHash_ = CityHash64(str.c_str(), str.size()); + } + fileCheckCounter_ = 0; + + fileInfo_ = engine_->FileInfo(); + + // Let's also trigger a reload, in case it changed. + g_Config.bReloadCheats = true; } void CwCheatScreen::CreateViews() { using namespace UI; auto cw = GetI18NCategory("CwCheats"); auto di = GetI18NCategory("Dialog"); - CreateCodeList(); - g_Config.bReloadCheats = true; - root_ = new LinearLayout(ORIENT_HORIZONTAL); + + root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + + LoadCheatInfo(); Margins actionMenuMargins(50, -15, 15, 0); LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(400, FILL_PARENT)); leftColumn->Add(new ItemHeader(cw->T("Options"))); - leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); //leftColumn->Add(new Choice(cw->T("Add Cheat")))->OnClick.Handle(this, &CwCheatScreen::OnAddCheat); leftColumn->Add(new Choice(cw->T("Import Cheats")))->OnClick.Handle(this, &CwCheatScreen::OnImportCheat); #if !defined(MOBILE_DEVICE) @@ -97,36 +91,49 @@ void CwCheatScreen::CreateViews() { leftColumn->Add(new Choice(cw->T("Enable/Disable All")))->OnClick.Handle(this, &CwCheatScreen::OnEnableAll); leftColumn->Add(new PopupSliderChoice(&g_Config.iCwCheatRefreshRate, 1, 1000, cw->T("Refresh Rate"), 1, screenManager())); - rightScroll_ = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(0.5f)); + rightScroll_ = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 0.5f)); rightScroll_->SetTag("CwCheats"); rightScroll_->SetScrollToTop(false); rightScroll_->ScrollTo(g_Config.fCwCheatScrollPosition); LinearLayout *rightColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(200, FILL_PARENT, actionMenuMargins)); - LayoutParams *layout = new LayoutParams(500, 50, LP_PLAIN); rightScroll_->Add(rightColumn); - root_->Add(leftColumn); - root_->Add(rightScroll_); rightColumn->Add(new ItemHeader(cw->T("Cheats"))); - for (size_t i = 0; i < formattedList_.size(); i++) { - name = formattedList_[i].c_str(); - rightColumn->Add(new CheatCheckBox(&bEnableCheat[i], cw->T(name), ""))->OnClick.Handle(this, &CwCheatScreen::OnCheckBox); + for (size_t i = 0; i < fileInfo_.size(); ++i) { + rightColumn->Add(new CheckBox(&fileInfo_[i].enabled, fileInfo_[i].name))->OnClick.Add([=](UI::EventParams &) { + return OnCheckBox((int)i); + }); } + + LinearLayout *layout = new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); + layout->Add(leftColumn); + layout->Add(rightScroll_); + root_->Add(layout); + + AddStandardBack(root_); +} + +void CwCheatScreen::update() { + if (fileCheckCounter_++ >= FILE_CHECK_FRAME_INTERVAL && engine_) { + // Check if the file has changed. If it has, we'll reload. + std::string str; + if (readFileToString(true, engine_->CheatFilename().c_str(), str)) { + uint64_t newHash = CityHash64(str.c_str(), str.size()); + if (newHash != fileCheckHash_) { + // This will update the hash. + RecreateViews(); + } + } + fileCheckCounter_ = 0; + } + + UIDialogScreenWithBackground::update(); } void CwCheatScreen::onFinish(DialogResult result) { - std::fstream fs; if (result != DR_BACK) // This only works for BACK here. return; - File::OpenCPPFile(fs, activeCheatFile, std::ios::out); - for (int j = 0; j < (int)cheatList.size(); j++) { - fs << cheatList[j]; - if (j < (int)cheatList.size() - 1) { - fs << "\n"; - } - } - fs.close(); - g_Config.bReloadCheats = true; + if (MIPSComp::jit) { MIPSComp::jit->ClearCache(); } @@ -134,30 +141,20 @@ void CwCheatScreen::onFinish(DialogResult result) { } UI::EventReturn CwCheatScreen::OnEnableAll(UI::EventParams ¶ms) { - std::fstream fs; - enableAll = !enableAll; - File::OpenCPPFile(fs, activeCheatFile, std::ios::out); - for (int j = 0; j < (int)cheatList.size(); j++) { - if (cheatList[j][0] == '_' && cheatList[j][1] == 'C') { - if (cheatList[j][2] == '0' && enableAll) { - cheatList[j][2] = '1'; - } else if (cheatList[j][2] != '0' && !enableAll) { - cheatList[j][2] = '0'; - } - } - } - for (size_t y = 0; y < bEnableCheat.size(); y++) { - bEnableCheat[y] = enableAll; - } - for (int i = 0; i < (int)cheatList.size(); i++) { - fs << cheatList[i]; - if (i < (int)cheatList.size() - 1) { - fs << "\n"; - } - } - fs.close(); + enableAllFlag_ = !enableAllFlag_; + + // Flip all the switches. + for (auto &info : fileInfo_) { + info.enabled = enableAllFlag_; + } + + if (!RebuildCheatFile(INDEX_ALL)) { + // Probably the file was modified outside PPSSPP, refresh. + // TODO: Report error. + RecreateViews(); + return UI::EVENT_SKIPPED; + } - g_Config.bReloadCheats = true; return UI::EVENT_DONE; } @@ -172,18 +169,19 @@ UI::EventReturn CwCheatScreen::OnEditCheatFile(UI::EventParams ¶ms) { if (MIPSComp::jit) { MIPSComp::jit->ClearCache(); } - TriggerFinish(DR_OK); + if (engine_) { #if PPSSPP_PLATFORM(UWP) - LaunchBrowser(activeCheatFile.c_str()); + LaunchBrowser(engine_->CheatFilename().c_str()); #else - File::openIniFile(activeCheatFile); + File::openIniFile(engine_->CheatFilename()); #endif + } return UI::EVENT_DONE; } UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { - if (gameTitle.length() != 9) { - WARN_LOG(COMMON, "CWCHEAT: Incorrect ID(%s) - can't import cheats.", gameTitle.c_str()); + if (gameID_.length() != 9 || !engine_) { + WARN_LOG(COMMON, "CWCHEAT: Incorrect ID(%s) - can't import cheats.", gameID_.c_str()); return UI::EVENT_DONE; } std::string line; @@ -192,7 +190,7 @@ UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { std::vector newList; std::string cheatFile = GetSysDirectory(DIRECTORY_CHEATS) + "cheat.db"; - std::string gameID = StringFromFormat("_S %s-%s", gameTitle.substr(0, 4).c_str(), gameTitle.substr(4).c_str()); + std::string gameID = StringFromFormat("_S %s-%s", gameID_.substr(0, 4).c_str(), gameID_.substr(4).c_str()); std::fstream fs; File::OpenCPPFile(fs, cheatFile, std::ios::in); @@ -212,9 +210,9 @@ UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { getline(fs, line); } if (line[0] == '_' && line[1] == 'C') { - //Test if cheat already exists in cheatList - for (size_t j = 0; j < formattedList_.size(); j++) { - if (line.substr(4) == formattedList_[j]) { + // Test if cheat already exists. + for (const auto &existing : fileInfo_) { + if (line.substr(4) == existing.name) { finished = false; goto loop; } @@ -239,10 +237,10 @@ UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { } fs.close(); std::string title2; - File::OpenCPPFile(fs, activeCheatFile, std::ios::in); + File::OpenCPPFile(fs, engine_->CheatFilename(), std::ios::in); getline(fs, title2); fs.close(); - File::OpenCPPFile(fs, activeCheatFile, std::ios::out | std::ios::app); + File::OpenCPPFile(fs, engine_->CheatFilename(), std::ios::out | std::ios::app); auto it = title.begin(); if (((title2[0] == '_' && title2[1] != 'S') || title2[0] == '/' || title2[0] == '#') && it != title.end() && (++it) != title.end()) { @@ -261,71 +259,79 @@ UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { } } fs.close(); + g_Config.bReloadCheats = true; - //Need a better way to refresh the screen, rather than exiting and having to re-enter. - TriggerFinish(DR_OK); + RecreateViews(); return UI::EVENT_DONE; } -UI::EventReturn CwCheatScreen::OnCheckBox(UI::EventParams ¶ms) { +UI::EventReturn CwCheatScreen::OnCheckBox(int index) { + if (!RebuildCheatFile(index)) { + // TODO: Report error. Let's reload the file, presumably it changed. + RecreateViews(); + return UI::EVENT_SKIPPED; + } + return UI::EVENT_DONE; } -void CwCheatScreen::processFileOn(std::string activatedCheat) { +bool CwCheatScreen::RebuildCheatFile(int index) { std::fstream fs; - for (size_t i = 0; i < cheatList.size(); i++) { - if (cheatList[i].length() >= 4) { - if (cheatList[i].substr(4) == activatedCheat) { - cheatList[i] = "_C1 " + activatedCheat; - } - } + if (!engine_ || !File::OpenCPPFile(fs, engine_->CheatFilename(), std::ios::in)) { + return false; } - File::OpenCPPFile(fs, activeCheatFile, std::ios::out); - - for (size_t j = 0; j < cheatList.size(); j++) { - fs << cheatList[j]; - if (j < cheatList.size() - 1) { - fs << "\n"; - } + // In case lines were edited while we weren't looking, reload them. + std::vector lines; + for (; fs && !fs.eof(); ) { + std::string line; + std::getline(fs, line, '\n'); + lines.push_back(line); } fs.close(); -} -void CwCheatScreen::processFileOff(std::string deactivatedCheat) { - std::fstream fs; - for (size_t i = 0; i < cheatList.size(); i++) { - if (cheatList[i].length() >= 4) { - if (cheatList[i].substr(4) == deactivatedCheat) { - cheatList[i] = "_C0 " + deactivatedCheat; + auto updateLine = [&](const CheatFileInfo &info) { + // Line numbers start with one, not zero. + size_t lineIndex = info.lineNum - 1; + if (lines.size() > lineIndex) { + auto &line = lines[lineIndex]; + // This is the one to change. Let's see if it matches - maybe the file changed. + bool isCheatDef = line.find("_C") != line.npos; + bool hasCheatName = !info.name.empty() && line.find(info.name) != line.npos; + if (!isCheatDef || !hasCheatName) { + return false; + } + + line = (info.enabled ? "_C1 " : "_C0 ") + info.name; + return true; + } + return false; + }; + + if (index == INDEX_ALL) { + for (const auto &info : fileInfo_) { + // Bail out if any don't match with no changes. + if (!updateLine(info)) { + return false; } } - } - - File::OpenCPPFile(fs, activeCheatFile, std::ios::out); - - for (size_t j = 0; j < cheatList.size(); j++) { - fs << cheatList[j]; - if (j < cheatList.size() - 1) { - fs << "\n"; + } else { + if (!updateLine(fileInfo_[index])) { + return false; } } + + + if (!File::OpenCPPFile(fs, engine_->CheatFilename(), std::ios::out | std::ios::trunc)) { + return false; + } + + for (const auto &line : lines) { + fs << line << '\n'; + } fs.close(); + + // Cheats will need to be reparsed now. + g_Config.bReloadCheats = true; + return true; } - -void CheatCheckBox::Draw(UIContext &dc) { - ClickableItem::Draw(dc); - int paddingX = 16; - int paddingY = 12; - - ImageID image = *toggle_ ? dc.theme->checkOn : dc.theme->checkOff; - - UI::Style style = dc.theme->itemStyle; - if (!IsEnabled()) - style = dc.theme->itemDisabledStyle; - - dc.SetFontStyle(dc.theme->uiFont); - dc.DrawText(text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), style.fgColor, ALIGN_VCENTER); - dc.Draw()->DrawImage(image, bounds_.x2() - paddingX, bounds_.centerY(), 1.0f, style.fgColor, ALIGN_RIGHT | ALIGN_VCENTER); -} - diff --git a/UI/CwCheatScreen.h b/UI/CwCheatScreen.h index a51c94afbe..c1bc827c68 100644 --- a/UI/CwCheatScreen.h +++ b/UI/CwCheatScreen.h @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include #include "ui/view.h" @@ -22,61 +23,39 @@ #include "ui/ui_context.h" #include "UI/MiscScreens.h" -extern std::string activeCheatFile; -extern std::string gameTitle; +struct CheatFileInfo; +class CWCheatEngine; class CwCheatScreen : public UIDialogScreenWithBackground { public: - CwCheatScreen(std::string gamePath); - CwCheatScreen() {} - void CreateCodeList(); - void processFileOn(std::string activatedCheat); - void processFileOff(std::string deactivatedCheat); - const char * name; - std::string activatedCheat, deactivatedCheat; + CwCheatScreen(const std::string &gamePath); + ~CwCheatScreen(); + + void LoadCheatInfo(); + UI::EventReturn OnAddCheat(UI::EventParams ¶ms); UI::EventReturn OnImportCheat(UI::EventParams ¶ms); UI::EventReturn OnEditCheatFile(UI::EventParams ¶ms); UI::EventReturn OnEnableAll(UI::EventParams ¶ms); + void update() override; void onFinish(DialogResult result) override; + protected: void CreateViews() override; private: - UI::EventReturn OnCheckBox(UI::EventParams ¶ms); - std::vector formattedList_; - UI::ScrollView *rightScroll_; -}; - -// TODO: Instead just hook the OnClick event on a regular checkbox. -class CheatCheckBox : public UI::ClickableItem, public CwCheatScreen { -public: - CheatCheckBox(bool *toggle, const std::string &text, const std::string &smallText = "", UI::LayoutParams *layoutParams = 0) - : UI::ClickableItem(layoutParams), toggle_(toggle), text_(text) { - OnClick.Handle(this, &CheatCheckBox::OnClicked); - } - - virtual void Draw(UIContext &dc); - - UI::EventReturn OnClicked(UI::EventParams &e) { - bool temp = false; - if (toggle_) { - *toggle_ = !(*toggle_); - temp = *toggle_; - } - if (temp) { - activatedCheat = text_; - processFileOn(activatedCheat); - } else { - deactivatedCheat = text_; - processFileOff(deactivatedCheat); - } - return UI::EVENT_DONE; - } - -private: - bool *toggle_; - std::string text_; - std::string smallText_; + UI::EventReturn OnCheckBox(int index); + + enum { INDEX_ALL = -1 }; + bool RebuildCheatFile(int index); + + UI::ScrollView *rightScroll_ = nullptr; + CWCheatEngine *engine_ = nullptr; + std::vector fileInfo_; + std::string gamePath_; + std::string gameID_; + int fileCheckCounter_ = 0; + uint64_t fileCheckHash_; + bool enableAllFlag_ = false; }; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index cce96c14c0..d0ced2f2e5 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -261,8 +261,7 @@ void GameSettingsScreen::CreateViews() { return UI::EVENT_CONTINUE; }); softwareGPU->OnClick.Handle(this, &GameSettingsScreen::OnSoftwareRendering); - if (PSP_IsInited()) - softwareGPU->SetEnabled(false); + softwareGPU->SetEnabled(!PSP_IsInited()); } graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); @@ -825,6 +824,7 @@ void GameSettingsScreen::CreateViews() { memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnChangeMemStickDir); #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) SavePathInMyDocumentChoice = systemSettings->Add(new CheckBox(&installed_, sy->T("Save path in My Documents", "Save path in My Documents"))); + SavePathInMyDocumentChoice->SetEnabled(!PSP_IsInited()); SavePathInMyDocumentChoice->OnClick.Handle(this, &GameSettingsScreen::OnSavePathMydoc); SavePathInOtherChoice = systemSettings->Add(new CheckBox(&otherinstalled_, sy->T("Save path in installed.txt", "Save path in installed.txt"))); SavePathInOtherChoice->SetEnabled(false); @@ -840,7 +840,7 @@ void GameSettingsScreen::CreateViews() { if (!(File::Delete(PPSSPPpath + "installedTEMP.txt"))) SavePathInMyDocumentChoice->SetEnabled(false); else - SavePathInOtherChoice->SetEnabled(true); + SavePathInOtherChoice->SetEnabled(!PSP_IsInited()); } else SavePathInMyDocumentChoice->SetEnabled(false); } else { @@ -857,7 +857,7 @@ void GameSettingsScreen::CreateViews() { // Skip UTF-8 encoding bytes if there are any. There are 3 of them. if (tempString.substr(0, 3) == "\xEF\xBB\xBF") tempString = tempString.substr(3); - SavePathInOtherChoice->SetEnabled(true); + SavePathInOtherChoice->SetEnabled(!PSP_IsInited()); if (!(tempString == "")) { installed_ = false; otherinstalled_ = true; @@ -871,7 +871,7 @@ void GameSettingsScreen::CreateViews() { #endif #if defined(_M_X64) - systemSettings->Add(new CheckBox(&g_Config.bCacheFullIsoInRam, sy->T("Cache ISO in RAM", "Cache full ISO in RAM"))); + systemSettings->Add(new CheckBox(&g_Config.bCacheFullIsoInRam, sy->T("Cache ISO in RAM", "Cache full ISO in RAM")))->SetEnabled(!PSP_IsInited()); #endif systemSettings->Add(new ItemHeader(sy->T("Cheats", "Cheats (experimental, see forums)")));