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