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