CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/CwCheat.cpp
Views: 1401
1
#include <algorithm>
2
#include <cctype>
3
#include <cstdint>
4
#include <cstdio>
5
6
#include "Common/Data/Text/I18n.h"
7
#include "Common/StringUtils.h"
8
#include "Common/Serialize/Serializer.h"
9
#include "Common/Serialize/SerializeFuncs.h"
10
#include "Common/System/OSD.h"
11
#include "Common/File/FileUtil.h"
12
#include "Core/CoreTiming.h"
13
#include "Core/CoreParameter.h"
14
#include "Core/CwCheat.h"
15
#include "Core/Config.h"
16
#include "Core/MemMapHelpers.h"
17
#include "Core/MIPS/MIPS.h"
18
#include "Core/ELF/ParamSFO.h"
19
#include "Core/System.h"
20
#include "Core/HLE/sceCtrl.h"
21
#include "Core/MIPS/JitCommon/JitCommon.h"
22
#include "Core/RetroAchievements.h"
23
#include "GPU/Common/PostShader.h"
24
25
#ifdef _WIN32
26
#include "Common/Data/Encoding/Utf8.h"
27
#endif
28
29
static int CheatEvent = -1;
30
static CWCheatEngine *cheatEngine;
31
static bool cheatsEnabled;
32
using namespace SceCtrl;
33
34
void hleCheat(u64 userdata, int cyclesLate);
35
36
static inline std::string TrimString(const std::string &s) {
37
auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) {
38
// isspace() expects 0 - 255, so convert any sign-extended value.
39
return std::isspace(c & 0xFF);
40
});
41
auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c){
42
return std::isspace(c & 0xFF);
43
}).base();
44
return wsback > wsfront ? std::string(wsfront, wsback) : std::string();
45
}
46
47
class CheatFileParser {
48
public:
49
CheatFileParser(const Path &filename, const std::string &gameID = "") {
50
fp_ = File::OpenCFile(filename, "rt");
51
validGameID_ = ReplaceAll(gameID, "-", "");
52
}
53
~CheatFileParser() {
54
if (fp_)
55
fclose(fp_);
56
}
57
58
bool Parse();
59
60
std::vector<std::string> GetErrors() const {
61
return errors_;
62
}
63
64
std::vector<CheatCode> GetCheats() const {
65
return cheats_;
66
}
67
68
std::vector<CheatFileInfo> GetFileInfo() const {
69
return cheatInfo_;
70
}
71
72
protected:
73
void Flush();
74
void FlushCheatInfo();
75
void AddError(const std::string &msg);
76
void ParseLine(const std::string &line);
77
void ParseDataLine(const std::string &line, CheatCodeFormat format);
78
bool ValidateGameID(const std::string &gameID);
79
80
FILE *fp_ = nullptr;
81
std::string validGameID_;
82
83
int line_ = 0;
84
int games_ = 0;
85
std::vector<std::string> errors_;
86
std::vector<CheatFileInfo> cheatInfo_;
87
std::vector<CheatCode> cheats_;
88
std::vector<CheatLine> pendingLines_;
89
CheatCodeFormat codeFormat_ = CheatCodeFormat::UNDEFINED;
90
CheatFileInfo lastCheatInfo_;
91
bool gameEnabled_ = true;
92
bool gameRiskyEnabled_ = false;
93
bool cheatEnabled_ = false;
94
};
95
96
bool CheatFileParser::Parse() {
97
for (line_ = 1; fp_ && !feof(fp_); ++line_) {
98
char temp[2048];
99
char *tempLine = fgets(temp, sizeof(temp), fp_);
100
if (!tempLine)
101
continue;
102
103
// Detect UTF-8 BOM sequence, and ignore it.
104
if (line_ == 1 && memcmp(tempLine, "\xEF\xBB\xBF", 3) == 0)
105
tempLine += 3;
106
std::string line = TrimString(tempLine);
107
108
// Minimum length 5 is shortest possible _ lines name of the game "_G N+"
109
// and a minimum of 1 displayable character in cheat name string "_C0 1"
110
// which both equal to 5 characters.
111
if (line.length() >= 5 && line[0] == '_') {
112
ParseLine(line);
113
} else if (line.length() >= 2 && line[0] == '/' && line[1] == '/') {
114
// Comment, ignore.
115
} else if (line.length() >= 1 && line[0] == '#') {
116
// Comment, ignore.
117
} else if (line.length() > 0) {
118
errors_.push_back(StringFromFormat("Unrecognized content on line %d: expecting _", line_));
119
}
120
}
121
122
Flush();
123
124
return errors_.empty();
125
}
126
127
void CheatFileParser::Flush() {
128
if (!pendingLines_.empty()) {
129
FlushCheatInfo();
130
cheats_.push_back({ codeFormat_, pendingLines_ });
131
pendingLines_.clear();
132
}
133
codeFormat_ = CheatCodeFormat::UNDEFINED;
134
}
135
136
void CheatFileParser::FlushCheatInfo() {
137
if (lastCheatInfo_.lineNum != 0) {
138
cheatInfo_.push_back(lastCheatInfo_);
139
lastCheatInfo_ = { 0 };
140
}
141
}
142
143
void CheatFileParser::AddError(const std::string &err) {
144
errors_.push_back(StringFromFormat("Error on line %d: %s", line_, err.c_str()));
145
}
146
147
void CheatFileParser::ParseLine(const std::string &line) {
148
switch (line[1]) {
149
case 'S':
150
// Disc ID, validate (for multi-disc cheat files)?
151
Flush();
152
++games_;
153
154
if (ValidateGameID(line.substr(2))) {
155
if (gameRiskyEnabled_) {
156
// We found the right one, so let's not use this risky stuff.
157
cheats_.clear();
158
cheatInfo_.clear();
159
gameRiskyEnabled_ = false;
160
}
161
gameEnabled_ = true;
162
} else if (games_ == 1) {
163
// Old behavior was to ignore.
164
// For BC, let's allow if the game id doesn't match, but there's only one line.
165
gameRiskyEnabled_ = true;
166
gameEnabled_ = true;
167
} else {
168
if (gameRiskyEnabled_) {
169
// There are multiple games here, kill the risky stuff.
170
cheats_.clear();
171
cheatInfo_.clear();
172
gameRiskyEnabled_ = false;
173
}
174
gameEnabled_ = false;
175
}
176
return;
177
178
case 'G':
179
// Game title.
180
return;
181
182
case 'C':
183
Flush();
184
185
// Cheat name and activation status.
186
if (line.length() >= 3 && line[2] >= '1' && line[2] <= '9') {
187
lastCheatInfo_ = { line_, line.length() >= 5 ? line.substr(4) : "", true };
188
cheatEnabled_ = true;
189
} else if (line.length() >= 3 && line[2] == '0') {
190
lastCheatInfo_ = { line_, line.length() >= 5 ? line.substr(4) : "", false };
191
cheatEnabled_ = false;
192
} else {
193
AddError("could not parse cheat name line");
194
cheatEnabled_ = false;
195
return;
196
}
197
return;
198
199
case 'L':
200
// CwCheat data line.
201
ParseDataLine(line.substr(2), CheatCodeFormat::CWCHEAT);
202
return;
203
204
case 'M':
205
// TempAR data line.
206
ParseDataLine(line.substr(2), CheatCodeFormat::TEMPAR);
207
return;
208
209
default:
210
AddError("unknown line type");
211
return;
212
}
213
}
214
215
void CheatFileParser::ParseDataLine(const std::string &line, CheatCodeFormat format) {
216
if (codeFormat_ == CheatCodeFormat::UNDEFINED) {
217
codeFormat_ = format;
218
} else if (codeFormat_ != format) {
219
AddError("mixed code format (cwcheat/tempar)");
220
lastCheatInfo_ = { 0 };
221
pendingLines_.clear();
222
cheatEnabled_ = false;
223
}
224
225
if (!gameEnabled_) {
226
return;
227
}
228
if (!cheatEnabled_) {
229
FlushCheatInfo();
230
return;
231
}
232
233
CheatLine cheatLine;
234
int len = 0;
235
if (sscanf(line.c_str(), "%x %x %n", &cheatLine.part1, &cheatLine.part2, &len) == 2) {
236
if ((size_t)len < line.length()) {
237
AddError("junk after line data");
238
}
239
pendingLines_.push_back(cheatLine);
240
} else {
241
AddError("expecting two values");
242
}
243
}
244
245
bool CheatFileParser::ValidateGameID(const std::string &gameID) {
246
return validGameID_.empty() || ReplaceAll(TrimString(gameID), "-", "") == validGameID_;
247
}
248
249
static void __CheatStop() {
250
if (cheatEngine) {
251
delete cheatEngine;
252
cheatEngine = nullptr;
253
}
254
cheatsEnabled = false;
255
}
256
257
static void __CheatStart() {
258
__CheatStop();
259
260
cheatEngine = new CWCheatEngine(g_paramSFO.GetDiscID());
261
// This only generates ini files on boot, let's leave homebrew ini file for UI.
262
std::string realGameID = g_paramSFO.GetValueString("DISC_ID");
263
if (!realGameID.empty()) {
264
cheatEngine->CreateCheatFile();
265
}
266
267
cheatEngine->ParseCheats();
268
g_Config.bReloadCheats = false;
269
cheatsEnabled = true;
270
}
271
272
static int GetRefreshMs() {
273
int refresh = g_Config.iCwCheatRefreshIntervalMs;
274
275
if (!cheatsEnabled)
276
refresh = 1000;
277
278
// Horrible hack for Tony Hawk - Underground 2. See #3854. Avoids crashing somehow
279
// but still causes regular JIT invalidations which causes stutters.
280
if (PSP_CoreParameter().compat.flags().JitInvalidationHack) {
281
refresh = 2;
282
}
283
284
return refresh;
285
}
286
287
void __CheatInit() {
288
// Always register the event, want savestates to be compatible whether cheats on or off.
289
CheatEvent = CoreTiming::RegisterEvent("CheatEvent", &hleCheat);
290
291
if (g_Config.bEnableCheats) {
292
__CheatStart();
293
}
294
295
// Only check once a second for cheats to be enabled.
296
CoreTiming::ScheduleEvent(msToCycles(GetRefreshMs()), CheatEvent, 0);
297
}
298
299
void __CheatShutdown() {
300
__CheatStop();
301
}
302
303
void __CheatDoState(PointerWrap &p) {
304
auto s = p.Section("CwCheat", 0, 2);
305
if (!s) {
306
CheatEvent = -1;
307
CoreTiming::RestoreRegisterEvent(CheatEvent, "CheatEvent", &hleCheat);
308
return;
309
}
310
311
Do(p, CheatEvent);
312
CoreTiming::RestoreRegisterEvent(CheatEvent, "CheatEvent", &hleCheat);
313
314
if (s < 2) {
315
// Before this we didn't have a checkpoint, so reset didn't work.
316
// Let's just force one in.
317
CoreTiming::RemoveEvent(CheatEvent);
318
CoreTiming::ScheduleEvent(msToCycles(GetRefreshMs()), CheatEvent, 0);
319
}
320
}
321
322
void hleCheat(u64 userdata, int cyclesLate) {
323
bool shouldBeEnabled = !Achievements::HardcoreModeActive() && g_Config.bEnableCheats;
324
325
if (cheatsEnabled != shouldBeEnabled) {
326
// Okay, let's move to the desired state, then.
327
if (shouldBeEnabled) {
328
__CheatStart();
329
} else {
330
__CheatStop();
331
}
332
}
333
334
// Check periodically for cheats.
335
CoreTiming::ScheduleEvent(msToCycles(GetRefreshMs()), CheatEvent, 0);
336
337
if (PSP_CoreParameter().compat.flags().JitInvalidationHack) {
338
std::string gameTitle = g_paramSFO.GetValueString("DISC_ID");
339
340
// Horrible hack for Tony Hawk - Underground 2. See #3854. Avoids crashing somehow
341
// but still causes regular JIT invalidations which causes stutters.
342
if (gameTitle == "ULUS10014") {
343
cheatEngine->InvalidateICache(0x08865600, 72);
344
cheatEngine->InvalidateICache(0x08865690, 4);
345
} else if (gameTitle == "ULES00033" || gameTitle == "ULES00034" || gameTitle == "ULES00035") { // euro, also 34 and 35
346
cheatEngine->InvalidateICache(0x088655D8, 72);
347
cheatEngine->InvalidateICache(0x08865668, 4);
348
} else if (gameTitle == "ULUS10138") { // MTX MotoTrax US
349
cheatEngine->InvalidateICache(0x0886DCC0, 72);
350
cheatEngine->InvalidateICache(0x0886DC20, 4);
351
cheatEngine->InvalidateICache(0x0886DD40, 4);
352
} else if (gameTitle == "ULES00581") { // MTX MotoTrax EU (ported from US cwcheat codes)
353
cheatEngine->InvalidateICache(0x0886E1D8, 72);
354
cheatEngine->InvalidateICache(0x0886E138, 4);
355
cheatEngine->InvalidateICache(0x0886E258, 4);
356
}
357
}
358
359
if (!cheatEngine || !cheatsEnabled)
360
return;
361
362
if (g_Config.bReloadCheats) { //Checks if the "reload cheats" button has been pressed.
363
cheatEngine->ParseCheats();
364
g_Config.bReloadCheats = false;
365
}
366
cheatEngine->Run();
367
}
368
369
CWCheatEngine::CWCheatEngine(const std::string &gameID) : gameID_(gameID) {
370
filename_ = GetSysDirectory(DIRECTORY_CHEATS) / (gameID_ + ".ini");
371
}
372
373
void CWCheatEngine::CreateCheatFile() {
374
File::CreateFullPath(GetSysDirectory(DIRECTORY_CHEATS));
375
376
if (!File::Exists(filename_)) {
377
FILE *f = File::OpenCFile(filename_, "wb");
378
if (f) {
379
fwrite("\xEF\xBB\xBF\n", 1, 4, f);
380
fclose(f);
381
}
382
if (!File::Exists(filename_)) {
383
auto err = GetI18NCategory(I18NCat::ERRORS);
384
g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Unable to create cheat file, disk may be full"));
385
}
386
}
387
}
388
389
Path CWCheatEngine::CheatFilename() {
390
return filename_;
391
}
392
393
void CWCheatEngine::ParseCheats() {
394
CheatFileParser parser(filename_, gameID_);
395
396
parser.Parse();
397
// TODO: Report errors.
398
399
cheats_ = parser.GetCheats();
400
}
401
402
u32 CWCheatEngine::GetAddress(u32 value) {
403
// Returns static address used by ppsspp. Some games may not like this, and causes cheats to not work without offset
404
u32 address = (value + 0x08800000) & 0x3FFFFFFF;
405
return address;
406
}
407
408
std::vector<CheatFileInfo> CWCheatEngine::FileInfo() {
409
CheatFileParser parser(filename_, gameID_);
410
411
parser.Parse();
412
return parser.GetFileInfo();
413
}
414
415
void CWCheatEngine::InvalidateICache(u32 addr, int size) {
416
// Round start down and size up to the nearest word.
417
u32 aligned = addr & ~3;
418
int alignedSize = (addr + size - aligned + 3) & ~3;
419
currentMIPS->InvalidateICache(aligned, alignedSize);
420
}
421
422
enum class CheatOp {
423
Invalid,
424
Noop,
425
426
Write,
427
Add,
428
Subtract,
429
Or,
430
And,
431
Xor,
432
433
MultiWrite,
434
435
CopyBytesFrom,
436
Vibration,
437
VibrationFromMemory,
438
PostShader,
439
PostShaderFromMemory,
440
Delay,
441
442
Assert,
443
444
IfEqual,
445
IfNotEqual,
446
IfLess,
447
IfGreater,
448
449
IfAddrEqual,
450
IfAddrNotEqual,
451
IfAddrLess,
452
IfAddrGreater,
453
454
IfPressed,
455
IfNotPressed,
456
457
CwCheatPointerCommands,
458
};
459
460
struct CheatOperation {
461
CheatOp op;
462
uint32_t addr;
463
int sz;
464
uint32_t val;
465
466
union {
467
struct {
468
uint32_t count;
469
uint32_t step;
470
uint32_t add;
471
} multiWrite;
472
struct {
473
uint32_t destAddr;
474
} copyBytesFrom;
475
struct {
476
uint32_t skip;
477
} ifTypes;
478
struct {
479
uint32_t skip;
480
uint32_t compareAddr;
481
} ifAddrTypes;
482
struct {
483
int offset;
484
int baseOffset;
485
int count;
486
int type;
487
} pointerCommands;
488
struct {
489
uint16_t vibrL;
490
uint16_t vibrR;
491
uint8_t vibrLTime;
492
uint8_t vibrRTime;
493
} vibrationValues;
494
struct {
495
union {
496
float f;
497
uint32_t u;
498
} value;
499
uint8_t shader;
500
uint8_t uniform;
501
uint8_t format;
502
} PostShaderUniform;
503
};
504
};
505
506
CheatOperation CWCheatEngine::InterpretNextCwCheat(const CheatCode &cheat, size_t &i) {
507
const CheatLine &line1 = cheat.lines[i++];
508
const uint32_t &arg = line1.part2;
509
510
// Filled as needed.
511
u32 addr;
512
513
int type = line1.part1 >> 28;
514
switch (type) {
515
case 0x0: // Write 8-bit data (up to 4 bytes.)
516
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
517
if (arg & 0xFFFF0000)
518
return { CheatOp::Write, addr, 4, arg };
519
else if (arg & 0x0000FF00)
520
return { CheatOp::Write, addr, 2, arg };
521
else
522
return { CheatOp::Write, addr, 1, arg };
523
524
case 0x1: // Write 16-bit data.
525
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
526
return { CheatOp::Write, addr, 2, arg };
527
528
case 0x2: // Write 32-bit data.
529
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
530
return { CheatOp::Write, addr, 4, arg };
531
532
case 0x3: // Increment/decrement data.
533
addr = GetAddress(arg & 0x0FFFFFFF);
534
switch ((line1.part1 >> 20) & 0xF) {
535
case 1:
536
return { CheatOp::Add, addr, 1, line1.part1 & 0xFF };
537
case 2:
538
return { CheatOp::Subtract, addr, 1, line1.part1 & 0xFF };
539
case 3:
540
return { CheatOp::Add, addr, 2, line1.part1 & 0xFFFF };
541
case 4:
542
return { CheatOp::Subtract, addr, 2, line1.part1 & 0xFFFF };
543
case 5:
544
if (i < cheat.lines.size())
545
return { CheatOp::Add, addr, 4, cheat.lines[i++].part1 };
546
return { CheatOp::Invalid };
547
case 6:
548
if (i < cheat.lines.size())
549
return { CheatOp::Subtract, addr, 4, cheat.lines[i++].part1 };
550
return { CheatOp::Invalid };
551
552
default:
553
return { CheatOp::Invalid };
554
}
555
break;
556
557
case 0x4: // 32-bit multi-write patch data.
558
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
559
if (i < cheat.lines.size()) {
560
const CheatLine &line2 = cheat.lines[i++];
561
562
CheatOperation op = { CheatOp::MultiWrite, addr, 4, line2.part1 };
563
op.multiWrite.count = arg >> 16;
564
op.multiWrite.step = (arg & 0xFFFF) * 4;
565
op.multiWrite.add = line2.part2;
566
return op;
567
}
568
return { CheatOp::Invalid };
569
570
case 0x5: // Memcpy command.
571
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
572
if (i < cheat.lines.size()) {
573
const CheatLine &line2 = cheat.lines[i++];
574
575
CheatOperation op = { CheatOp::CopyBytesFrom, addr, 0, arg };
576
op.copyBytesFrom.destAddr = GetAddress(line2.part1 & 0x0FFFFFFF);
577
return op;
578
}
579
return { CheatOp::Invalid };
580
581
case 0x6: // Pointer commands.
582
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
583
if (i < cheat.lines.size()) {
584
const CheatLine &line2 = cheat.lines[i++];
585
int count = (line2.part1 & 0xFFFF) - 1;
586
587
// Validate lines to process - make sure we stay inside cheat.lines.
588
if (i + count > cheat.lines.size())
589
return { CheatOp::Invalid };
590
591
CheatOperation op = { CheatOp::CwCheatPointerCommands, addr, 0, arg };
592
op.pointerCommands.offset = (int)line2.part2;
593
// TODO: Verify sign handling. Is this really supposed to sign extend?
594
op.pointerCommands.baseOffset = ((int)line2.part1 >> 20) * 4;
595
op.pointerCommands.count = count;
596
op.pointerCommands.type = (line2.part1 >> 16) & 0xF;
597
return op;
598
}
599
return { CheatOp::Invalid };
600
601
case 0x7: // Boolean data operations.
602
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
603
switch (arg >> 16) {
604
case 0x0000: // 8-bit OR.
605
return { CheatOp::Or, addr, 1, arg & 0xFF };
606
case 0x0001: // 16-bit OR.
607
return { CheatOp::Or, addr, 2, arg & 0xFFFF };
608
case 0x0002: // 8-bit AND.
609
return { CheatOp::And, addr, 1, arg & 0xFF };
610
case 0x0003: // 16-bit AND.
611
return { CheatOp::And, addr, 2, arg & 0xFFFF };
612
case 0x0004: // 8-bit XOR.
613
return { CheatOp::Xor, addr, 1, arg & 0xFF };
614
case 0x0005: // 16-bit XOR.
615
return { CheatOp::Xor, addr, 2, arg & 0xFFFF };
616
}
617
return { CheatOp::Invalid };
618
619
case 0x8: // 8-bit or 16-bit multi-write patch data.
620
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
621
if (i < cheat.lines.size()) {
622
const CheatLine &line2 = cheat.lines[i++];
623
const bool is8Bit = (line2.part1 & 0xFFFF0000) == 0;
624
const uint32_t val = is8Bit ? (line2.part1 & 0xFF) : (line2.part1 & 0xFFFF);
625
626
CheatOperation op = { CheatOp::MultiWrite, addr, is8Bit ? 1 : 2, val };
627
op.multiWrite.count = arg >> 16;
628
op.multiWrite.step = (arg & 0xFFFF) * (is8Bit ? 1 : 2);
629
op.multiWrite.add = line2.part2;
630
return op;
631
}
632
return { CheatOp::Invalid };
633
634
case 0xA: // PPSSPP specific cheats
635
switch (line1.part1 >> 24 & 0xF) {
636
case 0x0: // 0x0 sets gamepad vibration by cheat parameters
637
{
638
CheatOperation op = { CheatOp::Vibration };
639
op.vibrationValues.vibrL = line1.part1 & 0x0000FFFF;
640
op.vibrationValues.vibrR = line1.part2 & 0x0000FFFF;
641
op.vibrationValues.vibrLTime = (line1.part1 >> 16) & 0x000000FF;
642
op.vibrationValues.vibrRTime = (line1.part2 >> 16) & 0x000000FF;
643
return op;
644
}
645
case 0x1: // 0x1 reads value for gamepad vibration from memory
646
addr = line1.part2;
647
return { CheatOp::VibrationFromMemory, addr };
648
case 0x2: // 0x2 sets postprocessing shader uniform
649
{
650
CheatOperation op = { CheatOp::PostShader };
651
op.PostShaderUniform.uniform = line1.part1 & 0x000000FF;
652
op.PostShaderUniform.shader = (line1.part1 >> 16) & 0x000000FF;
653
op.PostShaderUniform.value.u = line1.part2;
654
return op;
655
}
656
case 0x3: // 0x3 sets postprocessing shader uniform from memory
657
{
658
addr = line1.part2;
659
CheatOperation op = { CheatOp::PostShaderFromMemory, addr };
660
op.PostShaderUniform.uniform = line1.part1 & 0x000000FF;
661
op.PostShaderUniform.format = (line1.part1 >> 8) & 0x000000FF;
662
op.PostShaderUniform.shader = (line1.part1 >> 16) & 0x000000FF;
663
return op;
664
}
665
// Place for other PPSSPP specific cheats
666
default:
667
return { CheatOp::Invalid };
668
}
669
670
case 0xB: // Delay command.
671
return { CheatOp::Delay, 0, 0, arg };
672
673
case 0xC: // 32-bit equal check / code stopper.
674
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
675
return { CheatOp::Assert, addr, 4, arg };
676
677
case 0xD: // Line skip tests & joker codes.
678
switch (arg >> 28) {
679
case 0x0: // 16-bit next line skip test.
680
case 0x2: // 8-bit next line skip test.
681
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
682
{
683
const bool is8Bit = (arg >> 28) == 0x2;
684
const uint32_t val = is8Bit ? (arg & 0xFF) : (arg & 0xFFFF);
685
686
CheatOp opcode;
687
switch ((arg >> 20) & 0xF) {
688
case 0x0:
689
opcode = CheatOp::IfEqual;
690
break;
691
case 0x1:
692
opcode = CheatOp::IfNotEqual;
693
break;
694
case 0x2:
695
opcode = CheatOp::IfLess;
696
break;
697
case 0x3:
698
opcode = CheatOp::IfGreater;
699
break;
700
default:
701
return { CheatOp::Invalid };
702
}
703
704
CheatOperation op = { opcode, addr, is8Bit ? 1 : 2, val };
705
op.ifTypes.skip = 1;
706
return op;
707
}
708
709
case 0x1: // Joker code - button pressed.
710
case 0x3: // Inverse joker code - button not pressed.
711
{
712
bool pressed = (arg >> 28) == 0x1;
713
CheatOperation op = { pressed ? CheatOp::IfPressed : CheatOp::IfNotPressed, 0, 0, arg & 0x0FFFFFFF };
714
op.ifTypes.skip = (line1.part1 & 0xFF) + 1;
715
return op;
716
}
717
718
case 0x4: // Adress equal test.
719
case 0x5: // Address not equal test.
720
case 0x6: // Address less than test.
721
case 0x7: // Address greater than test.
722
addr = GetAddress(line1.part1 & 0x0FFFFFFF);
723
if (i < cheat.lines.size()) {
724
const CheatLine &line2 = cheat.lines[i++];
725
const int sz = 1 << (line2.part2 & 0xF);
726
727
CheatOp opcode;
728
switch (arg >> 28) {
729
case 0x4:
730
opcode = CheatOp::IfAddrEqual;
731
break;
732
case 0x5:
733
opcode = CheatOp::IfAddrNotEqual;
734
break;
735
case 0x6:
736
opcode = CheatOp::IfAddrLess;
737
break;
738
case 0x7:
739
opcode = CheatOp::IfAddrGreater;
740
break;
741
default:
742
return { CheatOp::Invalid };
743
}
744
745
CheatOperation op = { opcode, addr, sz, 0 };
746
op.ifAddrTypes.skip = line2.part1;
747
op.ifAddrTypes.compareAddr = GetAddress(arg & 0x0FFFFFFF);
748
return op;
749
}
750
return { CheatOp::Invalid };
751
752
default:
753
return { CheatOp::Invalid };
754
}
755
756
case 0xE: // Multiple line skip tests.
757
addr = GetAddress(arg & 0x0FFFFFFF);
758
{
759
const bool is8Bit = (line1.part1 >> 24) == 0xE1;
760
const uint32_t val = is8Bit ? (line1.part1 & 0xFF) : (line1.part1 & 0xFFFF);
761
762
CheatOp opcode;
763
switch (arg >> 28) {
764
case 0x0:
765
opcode = CheatOp::IfEqual;
766
break;
767
case 0x1:
768
opcode = CheatOp::IfNotEqual;
769
break;
770
case 0x2:
771
opcode = CheatOp::IfLess;
772
break;
773
case 0x3:
774
opcode = CheatOp::IfGreater;
775
break;
776
default:
777
return { CheatOp::Invalid };
778
}
779
780
CheatOperation op = { opcode, addr, is8Bit ? 1 : 2, val };
781
op.ifTypes.skip = (line1.part1 >> 16) & (is8Bit ? 0xFF : 0xFFF);
782
return op;
783
}
784
785
default:
786
return { CheatOp::Invalid };
787
}
788
}
789
790
CheatOperation CWCheatEngine::InterpretNextTempAR(const CheatCode &cheat, size_t &i) {
791
// TODO
792
return { CheatOp::Invalid };
793
}
794
795
CheatOperation CWCheatEngine::InterpretNextOp(const CheatCode &cheat, size_t &i) {
796
if (cheat.fmt == CheatCodeFormat::CWCHEAT)
797
return InterpretNextCwCheat(cheat, i);
798
else if (cheat.fmt == CheatCodeFormat::TEMPAR)
799
return InterpretNextTempAR(cheat, i);
800
else {
801
// This shouldn't happen, but apparently does: #14082
802
// Either I'm missing a path or we have memory corruption.
803
// Not sure whether to log here though, feels like we could end up with a
804
// ton of logspam...
805
return { CheatOp::Invalid };
806
}
807
}
808
809
void CWCheatEngine::ApplyMemoryOperator(const CheatOperation &op, uint32_t(*oper)(uint32_t, uint32_t)) {
810
if (Memory::IsValidRange(op.addr, op.sz)) {
811
InvalidateICache(op.addr, op.sz);
812
if (op.sz == 1)
813
Memory::Write_U8((u8)oper(Memory::Read_U8(op.addr), op.val), op.addr);
814
else if (op.sz == 2)
815
Memory::Write_U16((u16)oper(Memory::Read_U16(op.addr), op.val),op. addr);
816
else if (op.sz == 4)
817
Memory::Write_U32((u32)oper(Memory::Read_U32(op.addr), op.val), op.addr);
818
}
819
}
820
821
bool CWCheatEngine::TestIf(const CheatOperation &op, bool(*oper)(int, int)) {
822
if (Memory::IsValidRange(op.addr, op.sz)) {
823
InvalidateICache(op.addr, op.sz);
824
825
int memoryValue = 0;
826
if (op.sz == 1)
827
memoryValue = (int)Memory::Read_U8(op.addr);
828
else if (op.sz == 2)
829
memoryValue = (int)Memory::Read_U16(op.addr);
830
else if (op.sz == 4)
831
memoryValue = (int)Memory::Read_U32(op.addr);
832
833
return oper(memoryValue, (int)op.val);
834
}
835
return false;
836
}
837
838
bool CWCheatEngine::TestIfAddr(const CheatOperation &op, bool(*oper)(int, int)) {
839
if (Memory::IsValidRange(op.addr, op.sz) && Memory::IsValidRange(op.ifAddrTypes.compareAddr, op.sz)) {
840
InvalidateICache(op.addr, op.sz);
841
InvalidateICache(op.addr, op.ifAddrTypes.compareAddr);
842
843
int memoryValue1 = 0;
844
int memoryValue2 = 0;
845
if (op.sz == 1) {
846
memoryValue1 = (int)Memory::Read_U8(op.addr);
847
memoryValue2 = (int)Memory::Read_U8(op.ifAddrTypes.compareAddr);
848
} else if (op.sz == 2) {
849
memoryValue1 = (int)Memory::Read_U16(op.addr);
850
memoryValue2 = (int)Memory::Read_U16(op.ifAddrTypes.compareAddr);
851
} else if (op.sz == 4) {
852
memoryValue1 = (int)Memory::Read_U32(op.addr);
853
memoryValue2 = (int)Memory::Read_U32(op.ifAddrTypes.compareAddr);
854
}
855
856
return oper(memoryValue1, memoryValue2);
857
}
858
return false;
859
}
860
861
void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat, size_t &i) {
862
switch (op.op) {
863
case CheatOp::Invalid:
864
i = cheat.lines.size();
865
break;
866
867
case CheatOp::Noop:
868
break;
869
870
case CheatOp::Write:
871
if (Memory::IsValidRange(op.addr, op.sz)) {
872
InvalidateICache(op.addr, op.sz);
873
if (op.sz == 1)
874
Memory::Write_U8((u8)op.val, op.addr);
875
else if (op.sz == 2)
876
Memory::Write_U16((u16)op.val, op.addr);
877
else if (op.sz == 4)
878
Memory::Write_U32((u32)op.val, op.addr);
879
}
880
break;
881
882
case CheatOp::Add:
883
ApplyMemoryOperator(op, [](uint32_t a, uint32_t b) {
884
return a + b;
885
});
886
break;
887
888
case CheatOp::Subtract:
889
ApplyMemoryOperator(op, [](uint32_t a, uint32_t b) {
890
return a - b;
891
});
892
break;
893
894
case CheatOp::Or:
895
ApplyMemoryOperator(op, [](uint32_t a, uint32_t b) {
896
return a | b;
897
});
898
break;
899
900
case CheatOp::And:
901
ApplyMemoryOperator(op, [](uint32_t a, uint32_t b) {
902
return a & b;
903
});
904
break;
905
906
case CheatOp::Xor:
907
ApplyMemoryOperator(op, [](uint32_t a, uint32_t b) {
908
return a ^ b;
909
});
910
break;
911
912
case CheatOp::MultiWrite:
913
if (Memory::IsValidAddress(op.addr)) {
914
InvalidateICache(op.addr, op.multiWrite.count * op.multiWrite.step + op.sz);
915
916
uint32_t data = op.val;
917
uint32_t addr = op.addr;
918
for (uint32_t a = 0; a < op.multiWrite.count; a++) {
919
if (Memory::IsValidAddress(addr)) {
920
if (op.sz == 1)
921
Memory::Write_U8((u8)data, addr);
922
else if (op.sz == 2)
923
Memory::Write_U16((u16)data, addr);
924
else if (op.sz == 4)
925
Memory::Write_U32((u32)data, addr);
926
}
927
addr += op.multiWrite.step;
928
data += op.multiWrite.add;
929
}
930
}
931
break;
932
933
case CheatOp::CopyBytesFrom:
934
if (Memory::IsValidRange(op.addr, op.val) && Memory::IsValidRange(op.copyBytesFrom.destAddr, op.val)) {
935
InvalidateICache(op.addr, op.val);
936
InvalidateICache(op.copyBytesFrom.destAddr, op.val);
937
938
Memory::Memcpy(op.copyBytesFrom.destAddr, op.addr, op.val, "CwCheat");
939
}
940
break;
941
942
case CheatOp::Vibration:
943
if (op.vibrationValues.vibrL > 0) {
944
SetLeftVibration(op.vibrationValues.vibrL);
945
SetVibrationLeftDropout(op.vibrationValues.vibrLTime);
946
}
947
if (op.vibrationValues.vibrR > 0) {
948
SetRightVibration(op.vibrationValues.vibrR);
949
SetVibrationRightDropout(op.vibrationValues.vibrRTime);
950
}
951
break;
952
953
case CheatOp::VibrationFromMemory:
954
if (Memory::IsValidRange(op.addr, 8)) {
955
uint16_t checkLeftVibration = Memory::Read_U16(op.addr);
956
uint16_t checkRightVibration = Memory::Read_U16(op.addr + 0x2);
957
if (checkLeftVibration > 0) {
958
SetLeftVibration(checkLeftVibration);
959
SetVibrationLeftDropout(Memory::Read_U8(op.addr + 0x4));
960
}
961
if (checkRightVibration > 0) {
962
SetRightVibration(checkRightVibration);
963
SetVibrationRightDropout(Memory::Read_U8(op.addr + 0x6));
964
}
965
}
966
break;
967
968
case CheatOp::PostShader:
969
{
970
auto shaderChain = GetFullPostShadersChain(g_Config.vPostShaderNames);
971
if (op.PostShaderUniform.shader < shaderChain.size()) {
972
std::string shaderName = shaderChain[op.PostShaderUniform.shader]->section;
973
g_Config.mPostShaderSetting[StringFromFormat("%sSettingCurrentValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = op.PostShaderUniform.value.f;
974
}
975
}
976
break;
977
978
case CheatOp::PostShaderFromMemory:
979
{
980
auto shaderChain = GetFullPostShadersChain(g_Config.vPostShaderNames);
981
if (Memory::IsValidRange(op.addr, 4) && op.PostShaderUniform.shader < shaderChain.size()) {
982
union {
983
float f;
984
uint32_t u;
985
} value;
986
value.u = Memory::Read_U32(op.addr);
987
std::string shaderName = shaderChain[op.PostShaderUniform.shader]->section;
988
switch (op.PostShaderUniform.format) {
989
case 0:
990
g_Config.mPostShaderSetting[StringFromFormat("%sSettingCurrentValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u & 0x000000FF;
991
break;
992
case 1:
993
g_Config.mPostShaderSetting[StringFromFormat("%sSettingCurrentValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u & 0x0000FFFF;
994
break;
995
case 2:
996
g_Config.mPostShaderSetting[StringFromFormat("%sSettingCurrentValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u;
997
break;
998
case 3:
999
g_Config.mPostShaderSetting[StringFromFormat("%sSettingCurrentValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.f;
1000
break;
1001
}
1002
}
1003
}
1004
break;
1005
1006
case CheatOp::Delay:
1007
// TODO: Not supported.
1008
break;
1009
1010
case CheatOp::Assert:
1011
if (Memory::IsValidRange(op.addr, 4)) {
1012
InvalidateICache(op.addr, 4);
1013
if (Memory::Read_U32(op.addr) != op.val) {
1014
i = cheat.lines.size();
1015
}
1016
}
1017
break;
1018
1019
case CheatOp::IfEqual:
1020
if (!TestIf(op, [](int a, int b) { return a == b; })) {
1021
i += (size_t)op.ifTypes.skip;
1022
}
1023
break;
1024
1025
case CheatOp::IfNotEqual:
1026
if (!TestIf(op, [](int a, int b) { return a != b; })) {
1027
i += (size_t)op.ifTypes.skip;
1028
}
1029
break;
1030
1031
case CheatOp::IfLess:
1032
if (!TestIf(op, [](int a, int b) { return a < b; })) {
1033
i += (size_t)op.ifTypes.skip;
1034
}
1035
break;
1036
1037
case CheatOp::IfGreater:
1038
if (!TestIf(op, [](int a, int b) { return a > b; })) {
1039
i += (size_t)op.ifTypes.skip;
1040
}
1041
break;
1042
1043
case CheatOp::IfAddrEqual:
1044
if (!TestIfAddr(op, [](int a, int b) { return a == b; })) {
1045
i += (size_t)op.ifAddrTypes.skip;
1046
}
1047
break;
1048
1049
case CheatOp::IfAddrNotEqual:
1050
if (!TestIfAddr(op, [](int a, int b) { return a != b; })) {
1051
i += (size_t)op.ifAddrTypes.skip;
1052
}
1053
break;
1054
1055
case CheatOp::IfAddrLess:
1056
if (!TestIfAddr(op, [](int a, int b) { return a < b; })) {
1057
i += (size_t)op.ifAddrTypes.skip;
1058
}
1059
break;
1060
1061
case CheatOp::IfAddrGreater:
1062
if (!TestIfAddr(op, [](int a, int b) { return a > b; })) {
1063
i += (size_t)op.ifAddrTypes.skip;
1064
}
1065
break;
1066
1067
case CheatOp::IfPressed:
1068
// Button Code
1069
// SELECT 0x00000001
1070
// START 0x00000008
1071
// DPAD UP 0x00000010
1072
// DPAD RIGHT 0x00000020
1073
// DPAD DOWN 0x00000040
1074
// DPAD LEFT 0x00000080
1075
// L TRIGGER 0x00000100
1076
// R TRIGGER 0x00000200
1077
// TRIANGLE 0x00001000
1078
// CIRCLE 0x00002000
1079
// CROSS 0x00004000
1080
// SQUARE 0x00008000
1081
// HOME 0x00010000
1082
// HOLD 0x00020000
1083
// WLAN 0x00040000
1084
// REMOTE HOLD 0x00080000
1085
// VOLUME UP 0x00100000
1086
// VOLUME DOWN 0x00200000
1087
// SCREEN 0x00400000
1088
// NOTE 0x00800000
1089
if ((__CtrlPeekButtons() & op.val) != op.val) {
1090
i += (size_t)op.ifTypes.skip;
1091
}
1092
break;
1093
1094
case CheatOp::IfNotPressed:
1095
if ((__CtrlPeekButtons() & op.val) == op.val) {
1096
i += (size_t)op.ifTypes.skip;
1097
}
1098
break;
1099
1100
case CheatOp::CwCheatPointerCommands:
1101
{
1102
InvalidateICache(op.addr + op.pointerCommands.baseOffset, 4);
1103
u32 base = Memory::Read_U32(op.addr + op.pointerCommands.baseOffset);
1104
u32 val = op.val;
1105
int type = op.pointerCommands.type;
1106
for (int a = 0; a < op.pointerCommands.count; ++a) {
1107
const CheatLine &line = cheat.lines[i++];
1108
switch (line.part1 >> 28) {
1109
case 0x1: // type copy byte
1110
{
1111
InvalidateICache(op.addr, 4);
1112
u32 srcAddr = Memory::Read_U32(op.addr) + op.pointerCommands.offset;
1113
u32 dstAddr = Memory::Read_U32(op.addr + op.pointerCommands.baseOffset) + (line.part1 & 0x0FFFFFFF);
1114
if (Memory::IsValidRange(dstAddr, val) && Memory::IsValidRange(srcAddr, val)) {
1115
InvalidateICache(dstAddr, val);
1116
InvalidateICache(srcAddr, val);
1117
Memory::Memcpy(dstAddr, srcAddr, val, "CwCheat");
1118
}
1119
// Don't perform any further action.
1120
type = -1;
1121
}
1122
break;
1123
1124
case 0x2:
1125
case 0x3: // type pointer walk
1126
{
1127
int walkOffset = (int)line.part1 & 0x0FFFFFFF;
1128
if ((line.part1 >> 28) == 0x3) {
1129
walkOffset = -walkOffset;
1130
}
1131
InvalidateICache(base + walkOffset, 4);
1132
base = Memory::Read_U32(base + walkOffset);
1133
switch (line.part2 >> 28) {
1134
case 0x2:
1135
case 0x3: // type pointer walk
1136
walkOffset = line.part2 & 0x0FFFFFFF;
1137
if ((line.part2 >> 28) == 0x3) {
1138
walkOffset = -walkOffset;
1139
}
1140
InvalidateICache(base + walkOffset, 4);
1141
base = Memory::Read_U32(base + walkOffset);
1142
break;
1143
1144
default:
1145
// Unexpected value in cheat line?
1146
break;
1147
}
1148
}
1149
break;
1150
1151
case 0x9: // type multi address write
1152
base += line.part1 & 0x0FFFFFFF;
1153
val += line.part2;
1154
break;
1155
1156
default:
1157
// Unexpected value in cheat line?
1158
break;
1159
}
1160
}
1161
1162
switch (type) {
1163
case 0: // 8 bit write
1164
InvalidateICache(base + op.pointerCommands.offset, 1);
1165
Memory::Write_U8((u8)val, base + op.pointerCommands.offset);
1166
break;
1167
case 1: // 16-bit write
1168
InvalidateICache(base + op.pointerCommands.offset, 2);
1169
Memory::Write_U16((u16)val, base + op.pointerCommands.offset);
1170
break;
1171
case 2: // 32-bit write
1172
InvalidateICache(base + op.pointerCommands.offset, 4);
1173
Memory::Write_U32((u32)val, base + op.pointerCommands.offset);
1174
break;
1175
case 3: // 8 bit inverse write
1176
InvalidateICache(base - op.pointerCommands.offset, 1);
1177
Memory::Write_U8((u8)val, base - op.pointerCommands.offset);
1178
break;
1179
case 4: // 16-bit inverse write
1180
InvalidateICache(base - op.pointerCommands.offset, 2);
1181
Memory::Write_U16((u16)val, base - op.pointerCommands.offset);
1182
break;
1183
case 5: // 32-bit inverse write
1184
InvalidateICache(base - op.pointerCommands.offset, 4);
1185
Memory::Write_U32((u32)val, base - op.pointerCommands.offset);
1186
break;
1187
case -1: // Operation already performed, nothing to do
1188
break;
1189
}
1190
}
1191
break;
1192
1193
default:
1194
_assert_(false);
1195
}
1196
}
1197
1198
void CWCheatEngine::Run() {
1199
if (Achievements::HardcoreModeActive()) {
1200
return;
1201
}
1202
1203
for (CheatCode cheat : cheats_) {
1204
// InterpretNextOp and ExecuteOp move i.
1205
for (size_t i = 0; i < cheat.lines.size(); ) {
1206
CheatOperation op = InterpretNextOp(cheat, i);
1207
ExecuteOp(op, cheat, i);
1208
}
1209
}
1210
}
1211
1212
bool CWCheatEngine::HasCheats() {
1213
return !cheats_.empty();
1214
}
1215
1216
bool CheatsInEffect() {
1217
if (!cheatEngine || !cheatsEnabled || Achievements::HardcoreModeActive())
1218
return false;
1219
return cheatEngine->HasCheats();
1220
}
1221
1222