1 // Copyright (c) 2013- PPSSPP Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #include "ppsspp_config.h"
19 #include <algorithm>
20 #include <map>
21 #include <unordered_map>
22
23 #include "Common/Common.h"
24 #include "Common/Data/Convert/SmallDataConvert.h"
25 #include "Common/Log.h"
26 #include "Common/Swap.h"
27 #include "Core/Config.h"
28 #include "Core/Debugger/Breakpoints.h"
29 #include "Core/Debugger/MemBlockInfo.h"
30 #include "Core/Debugger/SymbolMap.h"
31 #include "Core/MemMap.h"
32 #include "Core/MIPS/JitCommon/JitCommon.h"
33 #include "Core/MIPS/MIPSCodeUtils.h"
34 #include "Core/MIPS/MIPSAnalyst.h"
35 #include "Core/HLE/ReplaceTables.h"
36 #include "Core/HLE/FunctionWrappers.h"
37
38 #include "GPU/Math3D.h"
39 #include "GPU/GPU.h"
40 #include "GPU/GPUInterface.h"
41 #include "GPU/GPUState.h"
42
43 #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
44 #include <emmintrin.h>
45 #endif
46
47 enum class GPUReplacementSkip {
48 MEMSET = 1,
49 MEMCPY = 2,
50 MEMMOVE = 4,
51 };
52
53 static int skipGPUReplacements = 0;
54
55 // I think these have to be pretty accurate as these are libc replacements,
56 // but we can probably get away with approximating the VFPU vsin/vcos and vrot
57 // pretty roughly.
Replace_sinf()58 static int Replace_sinf() {
59 float f = PARAMF(0);
60 RETURNF(sinf(f));
61 return 80; // guess number of cycles
62 }
63
Replace_cosf()64 static int Replace_cosf() {
65 float f = PARAMF(0);
66 RETURNF(cosf(f));
67 return 80; // guess number of cycles
68 }
69
Replace_tanf()70 static int Replace_tanf() {
71 float f = PARAMF(0);
72 RETURNF(tanf(f));
73 return 80; // guess number of cycles
74 }
75
Replace_acosf()76 static int Replace_acosf() {
77 float f = PARAMF(0);
78 RETURNF(acosf(f));
79 return 80; // guess number of cycles
80 }
81
Replace_asinf()82 static int Replace_asinf() {
83 float f = PARAMF(0);
84 RETURNF(asinf(f));
85 return 80; // guess number of cycles
86 }
87
Replace_atanf()88 static int Replace_atanf() {
89 float f = PARAMF(0);
90 RETURNF(atanf(f));
91 return 80; // guess number of cycles
92 }
93
Replace_sqrtf()94 static int Replace_sqrtf() {
95 float f = PARAMF(0);
96 RETURNF(sqrtf(f));
97 return 80; // guess number of cycles
98 }
99
Replace_atan2f()100 static int Replace_atan2f() {
101 float f1 = PARAMF(0);
102 float f2 = PARAMF(1);
103 RETURNF(atan2f(f1, f2));
104 return 120; // guess number of cycles
105 }
106
Replace_floorf()107 static int Replace_floorf() {
108 float f1 = PARAMF(0);
109 RETURNF(floorf(f1));
110 return 30; // guess number of cycles
111 }
112
Replace_ceilf()113 static int Replace_ceilf() {
114 float f1 = PARAMF(0);
115 RETURNF(ceilf(f1));
116 return 30; // guess number of cycles
117 }
118
119 // Should probably do JIT versions of this, possibly ones that only delegate
120 // large copies to a C function.
Replace_memcpy()121 static int Replace_memcpy() {
122 u32 destPtr = PARAM(0);
123 u32 srcPtr = PARAM(1);
124 u32 bytes = PARAM(2);
125 bool skip = false;
126 if (!bytes) {
127 RETURN(destPtr);
128 return 10;
129 }
130
131 // Some games use memcpy on executable code. We need to flush emuhack ops.
132 currentMIPS->InvalidateICache(srcPtr, bytes);
133 if ((skipGPUReplacements & (int)GPUReplacementSkip::MEMCPY) == 0) {
134 if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) {
135 skip = gpu->PerformMemoryCopy(destPtr, srcPtr, bytes);
136 }
137 }
138 if (!skip && bytes != 0) {
139 u8 *dst = Memory::GetPointer(destPtr);
140 const u8 *src = Memory::GetPointer(srcPtr);
141
142 if (!dst || !src) {
143 // Already logged.
144 } else if (std::min(destPtr, srcPtr) + bytes > std::max(destPtr, srcPtr)) {
145 // Overlap. Star Ocean breaks if it's not handled in 16 bytes blocks.
146 const u32 blocks = bytes & ~0x0f;
147 for (u32 offset = 0; offset < blocks; offset += 0x10) {
148 memcpy(dst + offset, src + offset, 0x10);
149 }
150 for (u32 offset = blocks; offset < bytes; ++offset) {
151 dst[offset] = src[offset];
152 }
153 } else {
154 memmove(dst, src, bytes);
155 }
156 }
157 RETURN(destPtr);
158
159 const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes);
160 NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size());
161 NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size());
162
163 // It's pretty common that games will copy video data.
164 if (tag == "ReplaceMemcpy/VideoDecode" || tag == "ReplaceMemcpy/VideoDecodeRange") {
165 if (bytes == 512 * 272 * 4) {
166 gpu->NotifyVideoUpload(destPtr, bytes, 512, GE_FORMAT_8888);
167 }
168 }
169
170 return 10 + bytes / 4; // approximation
171 }
172
Replace_memcpy_jak()173 static int Replace_memcpy_jak() {
174 u32 destPtr = PARAM(0);
175 u32 srcPtr = PARAM(1);
176 u32 bytes = PARAM(2);
177 bool skip = false;
178 if (bytes == 0) {
179 RETURN(destPtr);
180 return 5;
181 }
182 currentMIPS->InvalidateICache(srcPtr, bytes);
183 if ((skipGPUReplacements & (int)GPUReplacementSkip::MEMCPY) == 0) {
184 if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) {
185 skip = gpu->PerformMemoryCopy(destPtr, srcPtr, bytes);
186 }
187 }
188 if (!skip && bytes != 0) {
189 u8 *dst = Memory::GetPointer(destPtr);
190 const u8 *src = Memory::GetPointer(srcPtr);
191
192 if (!dst || !src) {
193 } else {
194 // Jak style overlap.
195 for (u32 i = 0; i < bytes; i++) {
196 dst[i] = src[i];
197 }
198 }
199 }
200
201 // Jak relies on more registers coming out right than the ABI specifies.
202 // See the disassembly of the function for the explanations for these...
203 currentMIPS->r[MIPS_REG_T0] = 0;
204 currentMIPS->r[MIPS_REG_A0] = -1;
205 currentMIPS->r[MIPS_REG_A2] = 0;
206 currentMIPS->r[MIPS_REG_A3] = destPtr + bytes;
207 RETURN(destPtr);
208
209 const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes);
210 NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size());
211 NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size());
212
213 // It's pretty common that games will copy video data.
214 if (tag == "ReplaceMemcpy/VideoDecode" || tag == "ReplaceMemcpy/VideoDecodeRange") {
215 if (bytes == 512 * 272 * 4) {
216 gpu->NotifyVideoUpload(destPtr, bytes, 512, GE_FORMAT_8888);
217 }
218 }
219
220 return 5 + bytes * 8 + 2; // approximation. This is a slow memcpy - a byte copy loop..
221 }
222
Replace_memcpy16()223 static int Replace_memcpy16() {
224 u32 destPtr = PARAM(0);
225 u32 srcPtr = PARAM(1);
226 u32 bytes = PARAM(2) * 16;
227 bool skip = false;
228
229 // Some games use memcpy on executable code. We need to flush emuhack ops.
230 currentMIPS->InvalidateICache(srcPtr, bytes);
231 if ((skipGPUReplacements & (int)GPUReplacementSkip::MEMCPY) == 0) {
232 if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) {
233 skip = gpu->PerformMemoryCopy(destPtr, srcPtr, bytes);
234 }
235 }
236 if (!skip && bytes != 0) {
237 u8 *dst = Memory::GetPointer(destPtr);
238 const u8 *src = Memory::GetPointer(srcPtr);
239 if (dst && src) {
240 memmove(dst, src, bytes);
241 }
242 }
243 RETURN(destPtr);
244
245 const std::string tag = "ReplaceMemcpy16/" + GetMemWriteTagAt(srcPtr, bytes);
246 NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size());
247 NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size());
248
249 return 10 + bytes / 4; // approximation
250 }
251
Replace_memcpy_swizzled()252 static int Replace_memcpy_swizzled() {
253 u32 destPtr = PARAM(0);
254 u32 srcPtr = PARAM(1);
255 u32 pitch = PARAM(2);
256 u32 h = PARAM(4);
257 if ((skipGPUReplacements & (int)GPUReplacementSkip::MEMCPY) == 0) {
258 if (Memory::IsVRAMAddress(srcPtr)) {
259 gpu->PerformMemoryDownload(srcPtr, pitch * h);
260 }
261 }
262 u8 *dstp = Memory::GetPointer(destPtr);
263 const u8 *srcp = Memory::GetPointer(srcPtr);
264
265 if (dstp && srcp) {
266 const u8 *ysrcp = srcp;
267 for (u32 y = 0; y < h; y += 8) {
268 const u8 *xsrcp = ysrcp;
269 for (u32 x = 0; x < pitch; x += 16) {
270 const u8 *src = xsrcp;
271 for (int n = 0; n < 8; ++n) {
272 memcpy(dstp, src, 16);
273 src += pitch;
274 dstp += 16;
275 }
276 xsrcp += 16;
277 }
278 ysrcp += 8 * pitch;
279 }
280 }
281
282 RETURN(0);
283
284 const std::string tag = "ReplaceMemcpySwizzle/" + GetMemWriteTagAt(srcPtr, pitch * h);
285 NotifyMemInfo(MemBlockFlags::READ, srcPtr, pitch * h, tag.c_str(), tag.size());
286 NotifyMemInfo(MemBlockFlags::WRITE, destPtr, pitch * h, tag.c_str(), tag.size());
287
288 return 10 + (pitch * h) / 4; // approximation
289 }
290
Replace_memmove()291 static int Replace_memmove() {
292 u32 destPtr = PARAM(0);
293 u32 srcPtr = PARAM(1);
294 u32 bytes = PARAM(2);
295 bool skip = false;
296
297 // Some games use memcpy on executable code. We need to flush emuhack ops.
298 if ((skipGPUReplacements & (int)GPUReplacementSkip::MEMMOVE) == 0) {
299 currentMIPS->InvalidateICache(srcPtr, bytes);
300 if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) {
301 skip = gpu->PerformMemoryCopy(destPtr, srcPtr, bytes);
302 }
303 }
304 if (!skip && bytes != 0) {
305 u8 *dst = Memory::GetPointer(destPtr);
306 const u8 *src = Memory::GetPointer(srcPtr);
307 if (dst && src) {
308 memmove(dst, src, bytes);
309 }
310 }
311 RETURN(destPtr);
312
313 const std::string tag = "ReplaceMemmove/" + GetMemWriteTagAt(srcPtr, bytes);
314 NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size());
315 NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size());
316
317 return 10 + bytes / 4; // approximation
318 }
319
Replace_memset()320 static int Replace_memset() {
321 u32 destPtr = PARAM(0);
322 u8 value = PARAM(1);
323 u32 bytes = PARAM(2);
324 bool skip = false;
325 if (Memory::IsVRAMAddress(destPtr) && (skipGPUReplacements & (int)GPUReplacementSkip::MEMSET) == 0) {
326 skip = gpu->PerformMemorySet(destPtr, value, bytes);
327 }
328 if (!skip && bytes != 0) {
329 u8 *dst = Memory::GetPointer(destPtr);
330 if (dst) {
331 memset(dst, value, bytes);
332 }
333 }
334 RETURN(destPtr);
335
336 NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemset");
337
338 return 10 + bytes / 4; // approximation
339 }
340
Replace_memset_jak()341 static int Replace_memset_jak() {
342 u32 destPtr = PARAM(0);
343 u8 value = PARAM(1);
344 u32 bytes = PARAM(2);
345
346 if (bytes == 0) {
347 RETURN(destPtr);
348 return 5;
349 }
350
351 bool skip = false;
352 if (Memory::IsVRAMAddress(destPtr) && (skipGPUReplacements & (int)GPUReplacementSkip::MEMSET) == 0) {
353 skip = gpu->PerformMemorySet(destPtr, value, bytes);
354 }
355 if (!skip && bytes != 0) {
356 u8 *dst = Memory::GetPointer(destPtr);
357 if (dst) {
358 memset(dst, value, bytes);
359 }
360 }
361
362 currentMIPS->r[MIPS_REG_T0] = destPtr + bytes;
363 currentMIPS->r[MIPS_REG_A2] = -1;
364 currentMIPS->r[MIPS_REG_A3] = -1;
365 RETURN(destPtr);
366
367 NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemset");
368
369 return 5 + bytes * 6 + 2; // approximation (hm, inspecting the disasm this should be 5 + 6 * bytes + 2, but this is what works..)
370 }
371
Replace_strlen()372 static int Replace_strlen() {
373 u32 srcPtr = PARAM(0);
374 const char *src = (const char *)Memory::GetPointer(srcPtr);
375 u32 len = src ? (u32)strlen(src) : 0UL;
376 RETURN(len);
377 return 7 + len * 4; // approximation
378 }
379
Replace_strcpy()380 static int Replace_strcpy() {
381 u32 destPtr = PARAM(0);
382 char *dst = (char *)Memory::GetPointer(destPtr);
383 const char *src = (const char *)Memory::GetPointer(PARAM(1));
384 if (dst && src) {
385 strcpy(dst, src);
386 }
387 RETURN(destPtr);
388 return 10; // approximation
389 }
390
Replace_strncpy()391 static int Replace_strncpy() {
392 u32 destPtr = PARAM(0);
393 char *dst = (char *)Memory::GetPointer(destPtr);
394 const char *src = (const char *)Memory::GetPointer(PARAM(1));
395 u32 bytes = PARAM(2);
396 if (dst && src && bytes != 0) {
397 strncpy(dst, src, bytes);
398 }
399 RETURN(destPtr);
400 return 10; // approximation
401 }
402
Replace_strcmp()403 static int Replace_strcmp() {
404 const char *a = (const char *)Memory::GetPointer(PARAM(0));
405 const char *b = (const char *)Memory::GetPointer(PARAM(1));
406 if (a && b) {
407 RETURN(strcmp(a, b));
408 } else {
409 RETURN(0);
410 }
411 return 10; // approximation
412 }
413
Replace_strncmp()414 static int Replace_strncmp() {
415 const char *a = (const char *)Memory::GetPointer(PARAM(0));
416 const char *b = (const char *)Memory::GetPointer(PARAM(1));
417 u32 bytes = PARAM(2);
418 if (a && b && bytes != 0) {
419 RETURN(strncmp(a, b, bytes));
420 } else {
421 RETURN(0);
422 }
423 return 10 + bytes / 4; // approximation
424 }
425
Replace_fabsf()426 static int Replace_fabsf() {
427 RETURNF(fabsf(PARAMF(0)));
428 return 4;
429 }
430
Replace_vmmul_q_transp()431 static int Replace_vmmul_q_transp() {
432 float_le *out = (float_le *)Memory::GetPointer(PARAM(0));
433 const float_le *a = (const float_le *)Memory::GetPointer(PARAM(1));
434 const float_le *b = (const float_le *)Memory::GetPointer(PARAM(2));
435
436 // TODO: Actually use an optimized matrix multiply here...
437 if (out && b && a) {
438 #ifdef COMMON_BIG_ENDIAN
439 float outn[16], an[16], bn[16];
440 for (int i = 0; i < 16; ++i) {
441 an[i] = a[i];
442 bn[i] = b[i];
443 }
444 Matrix4ByMatrix4(outn, bn, an);
445 for (int i = 0; i < 16; ++i) {
446 out[i] = outn[i];
447 }
448 #else
449 Matrix4ByMatrix4(out, b, a);
450 #endif
451 }
452 return 16;
453 }
454
455 // a0 = pointer to destination address
456 // a1 = matrix
457 // a2 = source address
Replace_gta_dl_write_matrix()458 static int Replace_gta_dl_write_matrix() {
459 u32_le *ptr = (u32_le *)Memory::GetPointer(PARAM(0));
460 u32_le *src = (u32_le *)Memory::GetPointer(PARAM(2));
461 u32 matrix = PARAM(1) << 24;
462
463 if (!ptr || !src) {
464 RETURN(0);
465 return 38;
466 }
467
468 u32_le *dest = (u32_le *)Memory::GetPointer(ptr[0]);
469 if (!dest) {
470 RETURN(0);
471 return 38;
472 }
473
474 #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
475 __m128i topBytes = _mm_set1_epi32(matrix);
476 __m128i m0 = _mm_loadu_si128((const __m128i *)src);
477 __m128i m1 = _mm_loadu_si128((const __m128i *)(src + 4));
478 __m128i m2 = _mm_loadu_si128((const __m128i *)(src + 8));
479 __m128i m3 = _mm_loadu_si128((const __m128i *)(src + 12));
480 m0 = _mm_or_si128(_mm_srli_epi32(m0, 8), topBytes);
481 m1 = _mm_or_si128(_mm_srli_epi32(m1, 8), topBytes);
482 m2 = _mm_or_si128(_mm_srli_epi32(m2, 8), topBytes);
483 m3 = _mm_or_si128(_mm_srli_epi32(m3, 8), topBytes);
484 // These three stores overlap by a word, due to the offsets.
485 _mm_storeu_si128((__m128i *)dest, m0);
486 _mm_storeu_si128((__m128i *)(dest + 3), m1);
487 _mm_storeu_si128((__m128i *)(dest + 6), m2);
488 // Store the last one in parts to not overwrite forwards (probably mostly risk free though)
489 _mm_storel_epi64((__m128i *)(dest + 9), m3);
490 m3 = _mm_srli_si128(m3, 8);
491 _mm_store_ss((float *)(dest + 11), _mm_castsi128_ps(m3));
492 #else
493 // Bit tricky to SIMD (note the offsets) but should be doable if not perfect
494 dest[0] = matrix | (src[0] >> 8);
495 dest[1] = matrix | (src[1] >> 8);
496 dest[2] = matrix | (src[2] >> 8);
497 dest[3] = matrix | (src[4] >> 8);
498 dest[4] = matrix | (src[5] >> 8);
499 dest[5] = matrix | (src[6] >> 8);
500 dest[6] = matrix | (src[8] >> 8);
501 dest[7] = matrix | (src[9] >> 8);
502 dest[8] = matrix | (src[10] >> 8);
503 dest[9] = matrix | (src[12] >> 8);
504 dest[10] = matrix | (src[13] >> 8);
505 dest[11] = matrix | (src[14] >> 8);
506 #endif
507
508 (*ptr) += 0x30;
509
510 RETURN(0);
511 return 38;
512 }
513
514
515 // TODO: Inline into a few NEON or SSE instructions - especially if a1 is a known immediate!
516 // Anyway, not sure if worth it. There's not that many matrices written per frame normally.
Replace_dl_write_matrix()517 static int Replace_dl_write_matrix() {
518 u32_le *dlStruct = (u32_le *)Memory::GetPointer(PARAM(0));
519 u32_le *src = (u32_le *)Memory::GetPointer(PARAM(2));
520
521 if (!dlStruct || !src) {
522 RETURN(0);
523 return 60;
524 }
525
526 u32_le *dest = (u32_le *)Memory::GetPointer(dlStruct[2]);
527 if (!dest) {
528 RETURN(0);
529 return 60;
530 }
531
532 u32 matrix = 0;
533 int count = 12;
534 switch (PARAM(1)) {
535 case 3:
536 matrix = 0x40000000; // tex mtx
537 break;
538 case 2:
539 matrix = 0x3A000000;
540 break;
541 case 1:
542 matrix = 0x3C000000;
543 break;
544 case 0:
545 matrix = 0x3E000000;
546 count = 16;
547 break;
548 }
549
550 *dest++ = matrix;
551 matrix += 0x01000000;
552
553 if (count == 16) {
554 // Ultra SIMD friendly! These intrinsics generate pretty much perfect code,
555 // no point in hand rolling.
556 #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
557 __m128i topBytes = _mm_set1_epi32(matrix);
558 __m128i m0 = _mm_loadu_si128((const __m128i *)src);
559 __m128i m1 = _mm_loadu_si128((const __m128i *)(src + 4));
560 __m128i m2 = _mm_loadu_si128((const __m128i *)(src + 8));
561 __m128i m3 = _mm_loadu_si128((const __m128i *)(src + 12));
562 m0 = _mm_or_si128(_mm_srli_epi32(m0, 8), topBytes);
563 m1 = _mm_or_si128(_mm_srli_epi32(m1, 8), topBytes);
564 m2 = _mm_or_si128(_mm_srli_epi32(m2, 8), topBytes);
565 m3 = _mm_or_si128(_mm_srli_epi32(m3, 8), topBytes);
566 _mm_storeu_si128((__m128i *)dest, m0);
567 _mm_storeu_si128((__m128i *)(dest + 4), m1);
568 _mm_storeu_si128((__m128i *)(dest + 8), m2);
569 _mm_storeu_si128((__m128i *)(dest + 12), m3);
570 #else
571 #if 0
572 //TODO: Finish NEON, make conditional somehow
573 uint32x4_t topBytes = vdupq_n_u32(matrix);
574 uint32x4_t m0 = vld1q_u32(dataPtr);
575 uint32x4_t m1 = vld1q_u32(dataPtr + 4);
576 uint32x4_t m2 = vld1q_u32(dataPtr + 8);
577 uint32x4_t m3 = vld1q_u32(dataPtr + 12);
578 m0 = vorr_u32(vsri_n_u32(m0, 8), topBytes); // TODO: look into VSRI
579 m1 = vorr_u32(vshr_n_u32(m1, 8), topBytes);
580 m2 = vorr_u32(vshr_n_u32(m2, 8), topBytes);
581 m3 = vorr_u32(vshr_n_u32(m3, 8), topBytes);
582 vst1q_u32(dlPtr, m0);
583 vst1q_u32(dlPtr + 4, m1);
584 vst1q_u32(dlPtr + 8, m2);
585 vst1q_u32(dlPtr + 12, m3);
586 #endif
587 for (int i = 0; i < count; i++) {
588 dest[i] = matrix | (src[i] >> 8);
589 }
590 #endif
591 } else {
592 #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
593 __m128i topBytes = _mm_set1_epi32(matrix);
594 __m128i m0 = _mm_loadu_si128((const __m128i *)src);
595 __m128i m1 = _mm_loadu_si128((const __m128i *)(src + 4));
596 __m128i m2 = _mm_loadu_si128((const __m128i *)(src + 8));
597 __m128i m3 = _mm_loadu_si128((const __m128i *)(src + 12));
598 m0 = _mm_or_si128(_mm_srli_epi32(m0, 8), topBytes);
599 m1 = _mm_or_si128(_mm_srli_epi32(m1, 8), topBytes);
600 m2 = _mm_or_si128(_mm_srli_epi32(m2, 8), topBytes);
601 m3 = _mm_or_si128(_mm_srli_epi32(m3, 8), topBytes);
602 // These three stores overlap by a word, due to the offsets.
603 _mm_storeu_si128((__m128i *)dest, m0);
604 _mm_storeu_si128((__m128i *)(dest + 3), m1);
605 _mm_storeu_si128((__m128i *)(dest + 6), m2);
606 // Store the last one in parts to not overwrite forwards (probably mostly risk free though)
607 _mm_storel_epi64((__m128i *)(dest + 9), m3);
608 m3 = _mm_srli_si128(m3, 8);
609 _mm_store_ss((float *)(dest + 11), _mm_castsi128_ps(m3));
610 #else
611 // Bit tricky to SIMD (note the offsets) but should be doable if not perfect
612 dest[0] = matrix | (src[0] >> 8);
613 dest[1] = matrix | (src[1] >> 8);
614 dest[2] = matrix | (src[2] >> 8);
615 dest[3] = matrix | (src[4] >> 8);
616 dest[4] = matrix | (src[5] >> 8);
617 dest[5] = matrix | (src[6] >> 8);
618 dest[6] = matrix | (src[8] >> 8);
619 dest[7] = matrix | (src[9] >> 8);
620 dest[8] = matrix | (src[10] >> 8);
621 dest[9] = matrix | (src[12] >> 8);
622 dest[10] = matrix | (src[13] >> 8);
623 dest[11] = matrix | (src[14] >> 8);
624 #endif
625 }
626
627 NotifyMemInfo(MemBlockFlags::READ, PARAM(2), count * sizeof(float), "ReplaceDLWriteMatrix");
628 NotifyMemInfo(MemBlockFlags::WRITE, PARAM(0) + 2 * sizeof(u32), sizeof(u32), "ReplaceDLWriteMatrix");
629 NotifyMemInfo(MemBlockFlags::WRITE, dlStruct[2], (count + 1) * sizeof(u32), "ReplaceDLWriteMatrix");
630
631 dlStruct[2] += (1 + count) * 4;
632 RETURN(dlStruct[2]);
633 return 60;
634 }
635
GetMIPSStaticAddress(u32 & addr,s32 lui_offset,s32 lw_offset)636 static bool GetMIPSStaticAddress(u32 &addr, s32 lui_offset, s32 lw_offset) {
637 const MIPSOpcode upper = Memory::Read_Instruction(currentMIPS->pc + lui_offset, true);
638 if (upper != MIPS_MAKE_LUI(MIPS_GET_RT(upper), upper & 0xffff)) {
639 return false;
640 }
641 const MIPSOpcode lower = Memory::Read_Instruction(currentMIPS->pc + lw_offset, true);
642 if (lower != MIPS_MAKE_LW(MIPS_GET_RT(lower), MIPS_GET_RS(lower), lower & 0xffff)) {
643 if (lower != MIPS_MAKE_ORI(MIPS_GET_RT(lower), MIPS_GET_RS(lower), lower & 0xffff)) {
644 return false;
645 }
646 }
647 addr = ((upper & 0xffff) << 16) + (s16)(lower & 0xffff);
648 return true;
649 }
650
GetMIPSGPAddress(u32 & addr,s32 offset)651 static bool GetMIPSGPAddress(u32 &addr, s32 offset) {
652 const MIPSOpcode loadOp = Memory::Read_Instruction(currentMIPS->pc + offset, true);
653 if (MIPS_GET_RS(loadOp) == MIPS_REG_GP) {
654 s16 gpoff = (s16)(u16)(loadOp & 0x0000FFFF);
655 addr = currentMIPS->r[MIPS_REG_GP] + gpoff;
656 return true;
657 }
658
659 return false;
660 }
661
Hook_godseaterburst_blit_texture()662 static int Hook_godseaterburst_blit_texture() {
663 u32 texaddr;
664 // Only if there's no texture.
665 if (!GetMIPSStaticAddress(texaddr, 0x000c, 0x0030)) {
666 return 0;
667 }
668 u32 fb_infoaddr;
669 if (Memory::Read_U32(texaddr) != 0 || !GetMIPSStaticAddress(fb_infoaddr, 0x01d0, 0x01d4)) {
670 return 0;
671 }
672
673 const u32 fb_info = Memory::Read_U32(fb_infoaddr);
674 const u32 fb_address = Memory::Read_U32(fb_info);
675 if (Memory::IsVRAMAddress(fb_address)) {
676 gpu->PerformMemoryDownload(fb_address, 0x00044000);
677 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "godseaterburst_blit_texture");
678 }
679 return 0;
680 }
681
Hook_hexyzforce_monoclome_thread()682 static int Hook_hexyzforce_monoclome_thread() {
683 u32 fb_info;
684 if (!GetMIPSStaticAddress(fb_info, -4, 0)) {
685 return 0;
686 }
687
688 const u32 fb_address = Memory::Read_U32(fb_info);
689 if (Memory::IsVRAMAddress(fb_address)) {
690 gpu->PerformMemoryDownload(fb_address, 0x00088000);
691 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "hexyzforce_monoclome_thread");
692 }
693 return 0;
694 }
695
Hook_starocean_write_stencil()696 static int Hook_starocean_write_stencil() {
697 const u32 fb_address = currentMIPS->r[MIPS_REG_T7];
698 if (Memory::IsVRAMAddress(fb_address)) {
699 gpu->PerformStencilUpload(fb_address, 0x00088000);
700 }
701 return 0;
702 }
703
Hook_topx_create_saveicon()704 static int Hook_topx_create_saveicon() {
705 const u32 fb_address = currentMIPS->r[MIPS_REG_V0];
706 if (Memory::IsVRAMAddress(fb_address)) {
707 gpu->PerformMemoryDownload(fb_address, 0x00044000);
708 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "topx_create_saveicon");
709 }
710 return 0;
711 }
712
Hook_ff1_battle_effect()713 static int Hook_ff1_battle_effect() {
714 const u32 fb_address = currentMIPS->r[MIPS_REG_A1];
715 if (Memory::IsVRAMAddress(fb_address)) {
716 gpu->PerformMemoryDownload(fb_address, 0x00088000);
717 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "ff1_battle_effect");
718 }
719 return 0;
720 }
721
Hook_dissidia_recordframe_avi()722 static int Hook_dissidia_recordframe_avi() {
723 // This is called once per frame, and records that frame's data to avi.
724 const u32 fb_address = currentMIPS->r[MIPS_REG_A1];
725 if (Memory::IsVRAMAddress(fb_address)) {
726 gpu->PerformMemoryDownload(fb_address, 0x00044000);
727 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "dissidia_recordframe_avi");
728 }
729 return 0;
730 }
731
Hook_brandish_download_frame()732 static int Hook_brandish_download_frame() {
733 u32 fb_infoaddr;
734 if (!GetMIPSStaticAddress(fb_infoaddr, 0x2c, 0x30)) {
735 return 0;
736 }
737 const u32 fb_info = Memory::Read_U32(fb_infoaddr);
738 const MIPSOpcode fb_index_load = Memory::Read_Instruction(currentMIPS->pc + 0x38, true);
739 if (fb_index_load != MIPS_MAKE_LW(MIPS_GET_RT(fb_index_load), MIPS_GET_RS(fb_index_load), fb_index_load & 0xffff)) {
740 return 0;
741 }
742 const int fb_index_offset = (s16)(fb_index_load & 0xffff);
743 const u32 fb_index = (Memory::Read_U32(fb_info + fb_index_offset) + 1) & 1;
744 const u32 fb_address = 0x4000000 + (0x44000 * fb_index);
745 const u32 dest_address = currentMIPS->r[MIPS_REG_A1];
746 if (Memory::IsRAMAddress(dest_address)) {
747 gpu->PerformMemoryDownload(fb_address, 0x00044000);
748 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "brandish_download_frame");
749 }
750 return 0;
751 }
752
Hook_growlanser_create_saveicon()753 static int Hook_growlanser_create_saveicon() {
754 const u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_SP] + 4);
755 const u32 fmt = Memory::Read_U32(currentMIPS->r[MIPS_REG_SP]);
756 const u32 sz = fmt == GE_FORMAT_8888 ? 0x00088000 : 0x00044000;
757 if (Memory::IsVRAMAddress(fb_address) && fmt <= 3) {
758 gpu->PerformMemoryDownload(fb_address, sz);
759 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, sz, "growlanser_create_saveicon");
760 }
761 return 0;
762 }
763
Hook_sd_gundam_g_generation_download_frame()764 static int Hook_sd_gundam_g_generation_download_frame() {
765 const u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_SP] + 8);
766 const u32 fmt = Memory::Read_U32(currentMIPS->r[MIPS_REG_SP] + 4);
767 const u32 sz = fmt == GE_FORMAT_8888 ? 0x00088000 : 0x00044000;
768 if (Memory::IsVRAMAddress(fb_address) && fmt <= 3) {
769 gpu->PerformMemoryDownload(fb_address, sz);
770 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, sz, "sd_gundam_g_generation_download_frame");
771 }
772 return 0;
773 }
774
Hook_narisokonai_download_frame()775 static int Hook_narisokonai_download_frame() {
776 const u32 fb_address = currentMIPS->r[MIPS_REG_V0];
777 if (Memory::IsVRAMAddress(fb_address)) {
778 gpu->PerformMemoryDownload(fb_address, 0x00044000);
779 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "narisokonai_download_frame");
780 }
781 return 0;
782 }
783
Hook_kirameki_school_life_download_frame()784 static int Hook_kirameki_school_life_download_frame() {
785 const u32 fb_address = currentMIPS->r[MIPS_REG_A2];
786 if (Memory::IsVRAMAddress(fb_address)) {
787 gpu->PerformMemoryDownload(fb_address, 0x00088000);
788 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kirameki_school_life_download_frame");
789 }
790 return 0;
791 }
792
Hook_orenoimouto_download_frame()793 static int Hook_orenoimouto_download_frame() {
794 const u32 fb_address = currentMIPS->r[MIPS_REG_A4];
795 if (Memory::IsVRAMAddress(fb_address)) {
796 gpu->PerformMemoryDownload(fb_address, 0x00088000);
797 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "orenoimouto_download_frame");
798 }
799 return 0;
800 }
801
Hook_sakurasou_download_frame()802 static int Hook_sakurasou_download_frame() {
803 const u32 fb_address = currentMIPS->r[MIPS_REG_V0];
804 if (Memory::IsVRAMAddress(fb_address)) {
805 gpu->PerformMemoryDownload(fb_address, 0x00088000);
806 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "sakurasou_download_frame");
807 }
808 return 0;
809 }
810
Hook_suikoden1_and_2_download_frame_1()811 static int Hook_suikoden1_and_2_download_frame_1() {
812 const u32 fb_address = currentMIPS->r[MIPS_REG_S4];
813 if (Memory::IsVRAMAddress(fb_address)) {
814 gpu->PerformMemoryDownload(fb_address, 0x00088000);
815 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "suikoden1_and_2_download_frame_1");
816 }
817 return 0;
818 }
819
Hook_suikoden1_and_2_download_frame_2()820 static int Hook_suikoden1_and_2_download_frame_2() {
821 const u32 fb_address = currentMIPS->r[MIPS_REG_S2];
822 if (Memory::IsVRAMAddress(fb_address)) {
823 gpu->PerformMemoryDownload(fb_address, 0x00088000);
824 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "suikoden1_and_2_download_frame_2");
825 }
826 return 0;
827 }
828
Hook_rezel_cross_download_frame()829 static int Hook_rezel_cross_download_frame() {
830 const u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_SP] + 0x1C);
831 const u32 fmt = Memory::Read_U32(currentMIPS->r[MIPS_REG_SP] + 0x14);
832 const u32 sz = fmt == GE_FORMAT_8888 ? 0x00088000 : 0x00044000;
833 if (Memory::IsVRAMAddress(fb_address) && fmt <= 3) {
834 gpu->PerformMemoryDownload(fb_address, sz);
835 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, sz, "rezel_cross_download_frame");
836 }
837 return 0;
838 }
839
Hook_kagaku_no_ensemble_download_frame()840 static int Hook_kagaku_no_ensemble_download_frame() {
841 const u32 fb_address = currentMIPS->r[MIPS_REG_V0];
842 if (Memory::IsVRAMAddress(fb_address)) {
843 gpu->PerformMemoryDownload(fb_address, 0x00088000);
844 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kagaku_no_ensemble_download_frame");
845 }
846 return 0;
847 }
848
Hook_soranokiseki_fc_download_frame()849 static int Hook_soranokiseki_fc_download_frame() {
850 const u32 fb_address = currentMIPS->r[MIPS_REG_A2];
851 if (Memory::IsVRAMAddress(fb_address)) {
852 gpu->PerformMemoryDownload(fb_address, 0x00044000);
853 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "soranokiseki_fc_download_frame");
854 }
855 return 0;
856 }
857
Hook_soranokiseki_sc_download_frame()858 static int Hook_soranokiseki_sc_download_frame() {
859 u32 fb_infoaddr;
860 if (!GetMIPSStaticAddress(fb_infoaddr, 0x28, 0x2C)) {
861 return 0;
862 }
863 const u32 fb_info = Memory::Read_U32(fb_infoaddr);
864 const MIPSOpcode fb_index_load = Memory::Read_Instruction(currentMIPS->pc + 0x34, true);
865 if (fb_index_load != MIPS_MAKE_LW(MIPS_GET_RT(fb_index_load), MIPS_GET_RS(fb_index_load), fb_index_load & 0xffff)) {
866 return 0;
867 }
868 const int fb_index_offset = (s16)(fb_index_load & 0xffff);
869 const u32 fb_index = (Memory::Read_U32(fb_info + fb_index_offset) + 1) & 1;
870 const u32 fb_address = 0x4000000 + (0x44000 * fb_index);
871 const u32 dest_address = currentMIPS->r[MIPS_REG_A1];
872 if (Memory::IsRAMAddress(dest_address)) {
873 gpu->PerformMemoryDownload(fb_address, 0x00044000);
874 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "soranokiseki_sc_download_frame");
875 }
876 return 0;
877 }
878
Hook_bokunonatsuyasumi4_download_frame()879 static int Hook_bokunonatsuyasumi4_download_frame() {
880 const u32 fb_address = currentMIPS->r[MIPS_REG_A3];
881 if (Memory::IsVRAMAddress(fb_address)) {
882 gpu->PerformMemoryDownload(fb_address, 0x00044000);
883 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "bokunonatsuyasumi4_download_frame");
884 }
885 return 0;
886 }
887
Hook_danganronpa2_1_download_frame()888 static int Hook_danganronpa2_1_download_frame() {
889 const u32 fb_base = currentMIPS->r[MIPS_REG_V0];
890 const u32 fb_offset = currentMIPS->r[MIPS_REG_V1];
891 const u32 fb_offset_fix = fb_offset & 0xFFFFFFFC;
892 const u32 fb_address = fb_base + fb_offset_fix;
893 if (Memory::IsVRAMAddress(fb_address)) {
894 gpu->PerformMemoryDownload(fb_address, 0x00088000);
895 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "danganronpa2_1_download_frame");
896 }
897 return 0;
898 }
899
Hook_danganronpa2_2_download_frame()900 static int Hook_danganronpa2_2_download_frame() {
901 const u32 fb_base = currentMIPS->r[MIPS_REG_V0];
902 const u32 fb_offset = currentMIPS->r[MIPS_REG_V1];
903 const u32 fb_offset_fix = fb_offset & 0xFFFFFFFC;
904 const u32 fb_address = fb_base + fb_offset_fix;
905 if (Memory::IsVRAMAddress(fb_address)) {
906 gpu->PerformMemoryDownload(fb_address, 0x00088000);
907 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "danganronpa2_2_download_frame");
908 }
909 return 0;
910 }
911
Hook_danganronpa1_1_download_frame()912 static int Hook_danganronpa1_1_download_frame() {
913 const u32 fb_base = currentMIPS->r[MIPS_REG_A5];
914 const u32 fb_offset = currentMIPS->r[MIPS_REG_V0];
915 const u32 fb_offset_fix = fb_offset & 0xFFFFFFFC;
916 const u32 fb_address = fb_base + fb_offset_fix;
917 if (Memory::IsVRAMAddress(fb_address)) {
918 gpu->PerformMemoryDownload(fb_address, 0x00088000);
919 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "danganronpa1_1_download_frame");
920 }
921 return 0;
922 }
923
Hook_danganronpa1_2_download_frame()924 static int Hook_danganronpa1_2_download_frame() {
925 const MIPSOpcode instruction = Memory::Read_Instruction(currentMIPS->pc + 0x8, true);
926 const int reg_num = instruction >> 11 & 31;
927 const u32 fb_base = currentMIPS->r[reg_num];
928 const u32 fb_offset = currentMIPS->r[MIPS_REG_V0];
929 const u32 fb_offset_fix = fb_offset & 0xFFFFFFFC;
930 const u32 fb_address = fb_base + fb_offset_fix;
931 if (Memory::IsVRAMAddress(fb_address)) {
932 gpu->PerformMemoryDownload(fb_address, 0x00088000);
933 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "danganronpa1_2_download_frame");
934 }
935 return 0;
936 }
937
Hook_kankabanchoutbr_download_frame()938 static int Hook_kankabanchoutbr_download_frame() {
939 const u32 fb_address = currentMIPS->r[MIPS_REG_A1];
940 if (Memory::IsVRAMAddress(fb_address)) {
941 gpu->PerformMemoryDownload(fb_address, 0x00044000);
942 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "kankabanchoutbr_download_frame");
943 }
944 return 0;
945 }
946
Hook_orenoimouto_download_frame_2()947 static int Hook_orenoimouto_download_frame_2() {
948 const u32 fb_address = currentMIPS->r[MIPS_REG_A4];
949 if (Memory::IsVRAMAddress(fb_address)) {
950 gpu->PerformMemoryDownload(fb_address, 0x00088000);
951 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "orenoimouto_download_frame_2");
952 }
953 return 0;
954 }
955
Hook_rewrite_download_frame()956 static int Hook_rewrite_download_frame() {
957 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
958 if (Memory::IsVRAMAddress(fb_address)) {
959 gpu->PerformMemoryDownload(fb_address, 0x00088000);
960 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "rewrite_download_frame");
961 }
962 return 0;
963 }
964
Hook_kudwafter_download_frame()965 static int Hook_kudwafter_download_frame() {
966 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
967 if (Memory::IsVRAMAddress(fb_address)) {
968 gpu->PerformMemoryDownload(fb_address, 0x00088000);
969 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kudwafter_download_frame");
970 }
971 return 0;
972 }
973
Hook_kumonohatateni_download_frame()974 static int Hook_kumonohatateni_download_frame() {
975 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
976 if (Memory::IsVRAMAddress(fb_address)) {
977 gpu->PerformMemoryDownload(fb_address, 0x00088000);
978 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kumonohatateni_download_frame");
979 }
980 return 0;
981 }
982
Hook_otomenoheihou_download_frame()983 static int Hook_otomenoheihou_download_frame() {
984 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
985 if (Memory::IsVRAMAddress(fb_address)) {
986 gpu->PerformMemoryDownload(fb_address, 0x00088000);
987 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "otomenoheihou_download_frame");
988 }
989 return 0;
990 }
991
Hook_grisaianokajitsu_download_frame()992 static int Hook_grisaianokajitsu_download_frame() {
993 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
994 if (Memory::IsVRAMAddress(fb_address)) {
995 gpu->PerformMemoryDownload(fb_address, 0x00088000);
996 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "grisaianokajitsu_download_frame");
997 }
998 return 0;
999 }
1000
Hook_kokoroconnect_download_frame()1001 static int Hook_kokoroconnect_download_frame() {
1002 const u32 fb_address = currentMIPS->r[MIPS_REG_A3];
1003 if (Memory::IsVRAMAddress(fb_address)) {
1004 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1005 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kokoroconnect_download_frame");
1006 }
1007 return 0;
1008 }
1009
Hook_toheart2_download_frame()1010 static int Hook_toheart2_download_frame() {
1011 const u32 fb_address = currentMIPS->r[MIPS_REG_A1];
1012 if (Memory::IsVRAMAddress(fb_address)) {
1013 gpu->PerformMemoryDownload(fb_address, 0x00044000);
1014 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "toheart2_download_frame");
1015 }
1016 return 0;
1017 }
1018
Hook_toheart2_download_frame_2()1019 static int Hook_toheart2_download_frame_2() {
1020 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
1021 if (Memory::IsVRAMAddress(fb_address)) {
1022 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1023 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "toheart2_download_frame_2");
1024 }
1025 return 0;
1026 }
1027
Hook_flowers_download_frame()1028 static int Hook_flowers_download_frame() {
1029 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
1030 if (Memory::IsVRAMAddress(fb_address)) {
1031 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1032 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "flowers_download_frame");
1033 }
1034 return 0;
1035 }
1036
Hook_motorstorm_download_frame()1037 static int Hook_motorstorm_download_frame() {
1038 const u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_A1] + 0x18);
1039 if (Memory::IsVRAMAddress(fb_address)) {
1040 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1041 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "motorstorm_download_frame");
1042 }
1043 return 0;
1044 }
1045
Hook_utawarerumono_download_frame()1046 static int Hook_utawarerumono_download_frame() {
1047 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
1048 if (Memory::IsVRAMAddress(fb_address)) {
1049 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1050 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "utawarerumono_download_frame");
1051 }
1052 return 0;
1053 }
1054
Hook_photokano_download_frame()1055 static int Hook_photokano_download_frame() {
1056 const u32 fb_address = currentMIPS->r[MIPS_REG_A1];
1057 if (Memory::IsVRAMAddress(fb_address)) {
1058 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1059 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "photokano_download_frame");
1060 }
1061 return 0;
1062 }
1063
Hook_photokano_download_frame_2()1064 static int Hook_photokano_download_frame_2() {
1065 const u32 fb_address = currentMIPS->r[MIPS_REG_A1];
1066 if (Memory::IsVRAMAddress(fb_address)) {
1067 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1068 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "photokano_download_frame_2");
1069 }
1070 return 0;
1071 }
1072
Hook_gakuenheaven_download_frame()1073 static int Hook_gakuenheaven_download_frame() {
1074 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
1075 if (Memory::IsVRAMAddress(fb_address)) {
1076 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1077 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "gakuenheaven_download_frame");
1078 }
1079 return 0;
1080 }
1081
Hook_youkosohitsujimura_download_frame()1082 static int Hook_youkosohitsujimura_download_frame() {
1083 const u32 fb_address = currentMIPS->r[MIPS_REG_V0];
1084 if (Memory::IsVRAMAddress(fb_address)) {
1085 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1086 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "youkosohitsujimura_download_frame");
1087 }
1088 return 0;
1089 }
1090
Hook_zettai_hero_update_minimap_tex()1091 static int Hook_zettai_hero_update_minimap_tex() {
1092 const MIPSOpcode storeOffset = Memory::Read_Instruction(currentMIPS->pc + 4, true);
1093 const uint32_t texAddr = currentMIPS->r[MIPS_REG_A0] + SignExtend16ToS32(storeOffset);
1094 const uint32_t texSize = 64 * 64 * 1;
1095 const uint32_t writeAddr = currentMIPS->r[MIPS_REG_V1] + SignExtend16ToS32(storeOffset);
1096 if (Memory::IsValidRange(texAddr, texSize) && writeAddr >= texAddr && writeAddr < texAddr + texSize) {
1097 const uint8_t currentValue = Memory::Read_U8(writeAddr);
1098 if (currentValue != currentMIPS->r[MIPS_REG_A3]) {
1099 gpu->InvalidateCache(texAddr, texSize, GPU_INVALIDATE_FORCE);
1100 }
1101 }
1102 return 0;
1103 }
1104
Hook_tonyhawkp8_upload_tutorial_frame()1105 static int Hook_tonyhawkp8_upload_tutorial_frame() {
1106 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
1107 if (Memory::IsVRAMAddress(fb_address)) {
1108 gpu->PerformMemoryUpload(fb_address, 0x00088000);
1109 }
1110 return 0;
1111 }
1112
Hook_sdgundamggenerationportable_download_frame()1113 static int Hook_sdgundamggenerationportable_download_frame() {
1114 const u32 fb_address = currentMIPS->r[MIPS_REG_A3];
1115 if (Memory::IsVRAMAddress(fb_address)) {
1116 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1117 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "sdgundamggenerationportable_download_frame");
1118 }
1119 return 0;
1120 }
1121
Hook_atvoffroadfurypro_download_frame()1122 static int Hook_atvoffroadfurypro_download_frame() {
1123 const u32 fb_address = currentMIPS->r[MIPS_REG_S2];
1124 const u32 fb_size = (currentMIPS->r[MIPS_REG_S4] >> 3) * currentMIPS->r[MIPS_REG_S3];
1125 if (Memory::IsVRAMAddress(fb_address)) {
1126 gpu->PerformMemoryDownload(fb_address, fb_size);
1127 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_size, "atvoffroadfurypro_download_frame");
1128 }
1129 return 0;
1130 }
1131
Hook_atvoffroadfuryblazintrails_download_frame()1132 static int Hook_atvoffroadfuryblazintrails_download_frame() {
1133 const u32 fb_address = currentMIPS->r[MIPS_REG_S5];
1134 const u32 fb_size = (currentMIPS->r[MIPS_REG_S3] >> 3) * currentMIPS->r[MIPS_REG_S2];
1135 if (Memory::IsVRAMAddress(fb_address)) {
1136 gpu->PerformMemoryDownload(fb_address, fb_size);
1137 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_size, "atvoffroadfuryblazintrails_download_frame");
1138 }
1139 return 0;
1140 }
1141
Hook_littlebustersce_download_frame()1142 static int Hook_littlebustersce_download_frame() {
1143 const u32 fb_address = currentMIPS->r[MIPS_REG_A0];
1144 if (Memory::IsVRAMAddress(fb_address)) {
1145 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1146 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "littlebustersce_download_frame");
1147 }
1148 return 0;
1149 }
1150
Hook_shinigamitoshoujo_download_frame()1151 static int Hook_shinigamitoshoujo_download_frame() {
1152 const u32 fb_address = currentMIPS->r[MIPS_REG_S2];
1153 if (Memory::IsVRAMAddress(fb_address)) {
1154 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1155 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "shinigamitoshoujo_download_frame");
1156 }
1157 return 0;
1158 }
1159
Hook_atvoffroadfuryprodemo_download_frame()1160 static int Hook_atvoffroadfuryprodemo_download_frame() {
1161 const u32 fb_address = currentMIPS->r[MIPS_REG_S5];
1162 const u32 fb_size = ((currentMIPS->r[MIPS_REG_A0] + currentMIPS->r[MIPS_REG_A1]) >> 3) * currentMIPS->r[MIPS_REG_S2];
1163 if (Memory::IsVRAMAddress(fb_address)) {
1164 gpu->PerformMemoryDownload(fb_address, fb_size);
1165 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_size, "atvoffroadfuryprodemo_download_frame");
1166 }
1167 return 0;
1168 }
1169
Hook_unendingbloodycall_download_frame()1170 static int Hook_unendingbloodycall_download_frame() {
1171 const u32 fb_address = currentMIPS->r[MIPS_REG_T3];
1172 if (Memory::IsVRAMAddress(fb_address)) {
1173 gpu->PerformMemoryDownload(fb_address, 0x00088000);
1174 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "unendingbloodycall_download_frame");
1175 }
1176 return 0;
1177 }
1178
Hook_omertachinmokunookitethelegacy_download_frame()1179 static int Hook_omertachinmokunookitethelegacy_download_frame() {
1180 const u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_SP] + 4);
1181 if (Memory::IsVRAMAddress(fb_address)) {
1182 gpu->PerformMemoryDownload(fb_address, 0x00044000);
1183 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "omertachinmokunookitethelegacy_download_frame");
1184 }
1185 return 0;
1186 }
1187
Hook_katamari_render_check()1188 static int Hook_katamari_render_check() {
1189 const u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_A0] + 0x3C);
1190 const u32 fbInfoPtr = Memory::Read_U32(currentMIPS->r[MIPS_REG_A0] + 0x40);
1191 if (Memory::IsVRAMAddress(fb_address) && fbInfoPtr != 0) {
1192 const u32 sizeInfoPtr = Memory::Read_U32(fbInfoPtr + 0x0C);
1193 // These are the values it uses to control the loop.
1194 // Width in memory appears to be stride / 8.
1195 const u32 width = Memory::Read_U16(sizeInfoPtr + 0x08) * 8;
1196 // Height in memory is also divided by 8 (but this one isn't hardcoded.)
1197 const u32 heightBlocks = Memory::Read_U16(sizeInfoPtr + 0x0A);
1198 // For some reason this is the number of heightBlocks less 1.
1199 const u32 heightBlockCount = Memory::Read_U8(fbInfoPtr + 0x08) + 1;
1200
1201 const u32 totalBytes = width * heightBlocks * heightBlockCount;
1202 gpu->PerformMemoryDownload(fb_address, totalBytes);
1203 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, totalBytes, "katamari_render_check");
1204 }
1205 return 0;
1206 }
1207
Hook_katamari_screenshot_to_565()1208 static int Hook_katamari_screenshot_to_565() {
1209 u32 fb_address;
1210 if (GetMIPSStaticAddress(fb_address, 0x0040, 0x0044)) {
1211 gpu->PerformMemoryDownload(0x04000000 | fb_address, 0x00088000);
1212 NotifyMemInfo(MemBlockFlags::WRITE, 0x04000000 | fb_address, 0x00088000, "katamari_screenshot_to_565");
1213 }
1214 return 0;
1215 }
1216
Hook_mytranwars_upload_frame()1217 static int Hook_mytranwars_upload_frame() {
1218 u32 fb_address = currentMIPS->r[MIPS_REG_S0];
1219 if (Memory::IsVRAMAddress(fb_address)) {
1220 gpu->PerformMemoryUpload(fb_address, 0x00088000);
1221 }
1222 return 0;
1223 }
1224
1225 static u32 marvelalliance1_copy_src = 0;
1226 static u32 marvelalliance1_copy_dst = 0;
1227 static u32 marvelalliance1_copy_size = 0;
1228
Hook_marvelalliance1_copy_a1_before()1229 static int Hook_marvelalliance1_copy_a1_before() {
1230 marvelalliance1_copy_src = currentMIPS->r[MIPS_REG_A1];
1231 marvelalliance1_copy_dst = currentMIPS->r[MIPS_REG_V1];
1232 marvelalliance1_copy_size = currentMIPS->r[MIPS_REG_V0] - currentMIPS->r[MIPS_REG_V1];
1233
1234 gpu->PerformMemoryDownload(marvelalliance1_copy_src, marvelalliance1_copy_size);
1235 NotifyMemInfo(MemBlockFlags::WRITE, marvelalliance1_copy_src, marvelalliance1_copy_size, "marvelalliance1_copy_a1_before");
1236
1237 return 0;
1238 }
1239
Hook_marvelalliance1_copy_a2_before()1240 static int Hook_marvelalliance1_copy_a2_before() {
1241 marvelalliance1_copy_src = currentMIPS->r[MIPS_REG_A2];
1242 marvelalliance1_copy_dst = currentMIPS->r[MIPS_REG_V0];
1243 marvelalliance1_copy_size = currentMIPS->r[MIPS_REG_A1] - currentMIPS->r[MIPS_REG_A2];
1244
1245 gpu->PerformMemoryDownload(marvelalliance1_copy_src, marvelalliance1_copy_size);
1246 NotifyMemInfo(MemBlockFlags::WRITE, marvelalliance1_copy_src, marvelalliance1_copy_size, "marvelalliance1_copy_a2_before");
1247
1248 return 0;
1249 }
1250
Hook_marvelalliance1_copy_after()1251 static int Hook_marvelalliance1_copy_after() {
1252 gpu->PerformMemoryUpload(marvelalliance1_copy_dst, marvelalliance1_copy_size);
1253 NotifyMemInfo(MemBlockFlags::READ, marvelalliance1_copy_dst, marvelalliance1_copy_size, "marvelalliance1_copy_after");
1254
1255 return 0;
1256 }
1257
Hook_starocean_clear_framebuf_before()1258 static int Hook_starocean_clear_framebuf_before() {
1259 skipGPUReplacements |= (int)GPUReplacementSkip::MEMSET;
1260 return 0;
1261 }
1262
Hook_starocean_clear_framebuf_after()1263 static int Hook_starocean_clear_framebuf_after() {
1264 skipGPUReplacements &= ~(int)GPUReplacementSkip::MEMSET;
1265
1266 // This hook runs after the copy, this is the final memcpy destination.
1267 u32 framebuf = currentMIPS->r[MIPS_REG_V0] - 512 * 4 * 271;
1268 u32 y_address, h_address;
1269
1270 if (GetMIPSGPAddress(y_address, -204) && GetMIPSGPAddress(h_address, -200)) {
1271 int y = (s16)Memory::Read_U16(y_address);
1272 int h = (s16)Memory::Read_U16(h_address);
1273
1274 DEBUG_LOG(HLE, "starocean_clear_framebuf() - %08x y=%d-%d", framebuf, y, h);
1275 // TODO: This is always clearing to 0, actually, which could be faster than an upload.
1276 gpu->PerformMemoryUpload(framebuf + 512 * y * 4, 512 * h * 4);
1277 }
1278 return 0;
1279 }
1280
Hook_motorstorm_pixel_read()1281 static int Hook_motorstorm_pixel_read() {
1282 u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_A0] + 0x18);
1283 u32 fb_height = Memory::Read_U16(currentMIPS->r[MIPS_REG_A0] + 0x26);
1284 u32 fb_stride = Memory::Read_U16(currentMIPS->r[MIPS_REG_A0] + 0x28);
1285 gpu->PerformMemoryDownload(fb_address, fb_height * fb_stride);
1286 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_height * fb_stride, "motorstorm_pixel_read");
1287 return 0;
1288 }
1289
Hook_worms_copy_normalize_alpha()1290 static int Hook_worms_copy_normalize_alpha() {
1291 // At this point in the function (0x0CC), s1 is the framebuf and a2 is the size.
1292 u32 fb_address = currentMIPS->r[MIPS_REG_S1];
1293 u32 fb_size = currentMIPS->r[MIPS_REG_A2];
1294 if (Memory::IsVRAMAddress(fb_address) && Memory::IsValidRange(fb_address, fb_size)) {
1295 gpu->PerformMemoryDownload(fb_address, fb_size);
1296 NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_size, "worms_copy_normalize_alpha");
1297 }
1298 return 0;
1299 }
1300
Hook_openseason_data_decode()1301 static int Hook_openseason_data_decode() {
1302 static u32 firstWritePtr = 0;
1303
1304 u32 curWritePtr = currentMIPS->r[MIPS_REG_A0];
1305 u32 endPtr = currentMIPS->r[MIPS_REG_A1];
1306 u32 writeBytes = currentMIPS->r[MIPS_REG_V0];
1307 u32 startPtr = curWritePtr - writeBytes;
1308 if (Memory::IsVRAMAddress(startPtr) && (firstWritePtr == 0 || startPtr < firstWritePtr)) {
1309 firstWritePtr = startPtr;
1310 }
1311 if (Memory::IsVRAMAddress(endPtr) && curWritePtr == endPtr) {
1312 gpu->PerformMemoryUpload(firstWritePtr, endPtr - firstWritePtr);
1313 firstWritePtr = 0;
1314 }
1315 return 0;
1316 }
1317
1318 #define JITFUNC(f) (&MIPSComp::MIPSFrontendInterface::f)
1319
1320 // Can either replace with C functions or functions emitted in Asm/ArmAsm.
1321 static const ReplacementTableEntry entries[] = {
1322 // TODO: I think some games can be helped quite a bit by implementing the
1323 // double-precision soft-float routines: __adddf3, __subdf3 and so on. These
1324 // should of course be implemented JIT style, inline.
1325
1326 /* These two collide (same hash) and thus can't be replaced :/
1327 { "asinf", &Replace_asinf, 0, REPFLAG_DISABLED },
1328 { "acosf", &Replace_acosf, 0, REPFLAG_DISABLED },
1329 */
1330
1331 { "sinf", &Replace_sinf, 0, REPFLAG_DISABLED },
1332 { "cosf", &Replace_cosf, 0, REPFLAG_DISABLED },
1333 { "tanf", &Replace_tanf, 0, REPFLAG_DISABLED },
1334 { "atanf", &Replace_atanf, 0, REPFLAG_DISABLED },
1335 { "sqrtf", &Replace_sqrtf, 0, REPFLAG_DISABLED },
1336 { "atan2f", &Replace_atan2f, 0, REPFLAG_DISABLED },
1337 { "floorf", &Replace_floorf, 0, REPFLAG_DISABLED },
1338 { "ceilf", &Replace_ceilf, 0, REPFLAG_DISABLED },
1339
1340 { "memcpy", &Replace_memcpy, 0, 0 },
1341 { "memcpy_jak", &Replace_memcpy_jak, 0, 0 },
1342 { "memcpy16", &Replace_memcpy16, 0, 0 },
1343 { "memcpy_swizzled", &Replace_memcpy_swizzled, 0, 0 },
1344 { "memmove", &Replace_memmove, 0, 0 },
1345 { "memset", &Replace_memset, 0, 0 },
1346 { "memset_jak", &Replace_memset_jak, 0, 0 },
1347 { "strlen", &Replace_strlen, 0, REPFLAG_DISABLED },
1348 { "strcpy", &Replace_strcpy, 0, REPFLAG_DISABLED },
1349 { "strncpy", &Replace_strncpy, 0, REPFLAG_DISABLED },
1350 { "strcmp", &Replace_strcmp, 0, REPFLAG_DISABLED },
1351 { "strncmp", &Replace_strncmp, 0, REPFLAG_DISABLED },
1352 { "fabsf", &Replace_fabsf, JITFUNC(Replace_fabsf), REPFLAG_ALLOWINLINE | REPFLAG_DISABLED },
1353 { "dl_write_matrix", &Replace_dl_write_matrix, 0, REPFLAG_DISABLED }, // &MIPSComp::Jit::Replace_dl_write_matrix, REPFLAG_DISABLED },
1354 { "dl_write_matrix_2", &Replace_dl_write_matrix, 0, REPFLAG_DISABLED },
1355 { "gta_dl_write_matrix", &Replace_gta_dl_write_matrix, 0, REPFLAG_DISABLED },
1356 // dl_write_matrix_3 doesn't take the dl as a parameter, it accesses a global instead. Need to extract the address of the global from the code when replacing...
1357 // Haven't investigated write_matrix_4 and 5 but I think they are similar to 1 and 2.
1358
1359 // { "vmmul_q_transp", &Replace_vmmul_q_transp, 0, REPFLAG_DISABLED },
1360
1361 { "godseaterburst_blit_texture", &Hook_godseaterburst_blit_texture, 0, REPFLAG_HOOKENTER },
1362 { "hexyzforce_monoclome_thread", &Hook_hexyzforce_monoclome_thread, 0, REPFLAG_HOOKENTER, 0x58 },
1363 { "starocean_write_stencil", &Hook_starocean_write_stencil, 0, REPFLAG_HOOKENTER, 0x260 },
1364 { "topx_create_saveicon", &Hook_topx_create_saveicon, 0, REPFLAG_HOOKENTER, 0x34 },
1365 { "ff1_battle_effect", &Hook_ff1_battle_effect, 0, REPFLAG_HOOKENTER },
1366 // This is actually used in other games, not just Dissidia.
1367 { "dissidia_recordframe_avi", &Hook_dissidia_recordframe_avi, 0, REPFLAG_HOOKENTER },
1368 { "brandish_download_frame", &Hook_brandish_download_frame, 0, REPFLAG_HOOKENTER },
1369 { "growlanser_create_saveicon", &Hook_growlanser_create_saveicon, 0, REPFLAG_HOOKENTER, 0x7C },
1370 { "sd_gundam_g_generation_download_frame", &Hook_sd_gundam_g_generation_download_frame, 0, REPFLAG_HOOKENTER, 0x48},
1371 { "narisokonai_download_frame", &Hook_narisokonai_download_frame, 0, REPFLAG_HOOKENTER, 0x14 },
1372 { "kirameki_school_life_download_frame", &Hook_kirameki_school_life_download_frame, 0, REPFLAG_HOOKENTER },
1373 { "orenoimouto_download_frame", &Hook_orenoimouto_download_frame, 0, REPFLAG_HOOKENTER },
1374 { "sakurasou_download_frame", &Hook_sakurasou_download_frame, 0, REPFLAG_HOOKENTER, 0xF8 },
1375 { "suikoden1_and_2_download_frame_1", &Hook_suikoden1_and_2_download_frame_1, 0, REPFLAG_HOOKENTER, 0x9C },
1376 { "suikoden1_and_2_download_frame_2", &Hook_suikoden1_and_2_download_frame_2, 0, REPFLAG_HOOKENTER, 0x48 },
1377 { "rezel_cross_download_frame", &Hook_rezel_cross_download_frame, 0, REPFLAG_HOOKENTER, 0x54 },
1378 { "kagaku_no_ensemble_download_frame", &Hook_kagaku_no_ensemble_download_frame, 0, REPFLAG_HOOKENTER, 0x38 },
1379 { "soranokiseki_fc_download_frame", &Hook_soranokiseki_fc_download_frame, 0, REPFLAG_HOOKENTER, 0x180 },
1380 { "soranokiseki_sc_download_frame", &Hook_soranokiseki_sc_download_frame, 0, REPFLAG_HOOKENTER, },
1381 { "bokunonatsuyasumi4_download_frame", &Hook_bokunonatsuyasumi4_download_frame, 0, REPFLAG_HOOKENTER, 0x8C },
1382 { "danganronpa2_1_download_frame", &Hook_danganronpa2_1_download_frame, 0, REPFLAG_HOOKENTER, 0x68 },
1383 { "danganronpa2_2_download_frame", &Hook_danganronpa2_2_download_frame, 0, REPFLAG_HOOKENTER, 0x94 },
1384 { "danganronpa1_1_download_frame", &Hook_danganronpa1_1_download_frame, 0, REPFLAG_HOOKENTER, 0x78 },
1385 { "danganronpa1_2_download_frame", &Hook_danganronpa1_2_download_frame, 0, REPFLAG_HOOKENTER, 0xA8 },
1386 { "kankabanchoutbr_download_frame", &Hook_kankabanchoutbr_download_frame, 0, REPFLAG_HOOKENTER, },
1387 { "orenoimouto_download_frame_2", &Hook_orenoimouto_download_frame_2, 0, REPFLAG_HOOKENTER, },
1388 { "rewrite_download_frame", &Hook_rewrite_download_frame, 0, REPFLAG_HOOKENTER, 0x5C },
1389 { "kudwafter_download_frame", &Hook_kudwafter_download_frame, 0, REPFLAG_HOOKENTER, 0x58 },
1390 { "kumonohatateni_download_frame", &Hook_kumonohatateni_download_frame, 0, REPFLAG_HOOKENTER, },
1391 { "otomenoheihou_download_frame", &Hook_otomenoheihou_download_frame, 0, REPFLAG_HOOKENTER, 0x14 },
1392 { "grisaianokajitsu_download_frame", &Hook_grisaianokajitsu_download_frame, 0, REPFLAG_HOOKENTER, 0x14 },
1393 { "kokoroconnect_download_frame", &Hook_kokoroconnect_download_frame, 0, REPFLAG_HOOKENTER, 0x60 },
1394 { "toheart2_download_frame", &Hook_toheart2_download_frame, 0, REPFLAG_HOOKENTER, },
1395 { "toheart2_download_frame_2", &Hook_toheart2_download_frame_2, 0, REPFLAG_HOOKENTER, 0x18 },
1396 { "flowers_download_frame", &Hook_flowers_download_frame, 0, REPFLAG_HOOKENTER, 0x44 },
1397 { "motorstorm_download_frame", &Hook_motorstorm_download_frame, 0, REPFLAG_HOOKENTER, },
1398 { "utawarerumono_download_frame", &Hook_utawarerumono_download_frame, 0, REPFLAG_HOOKENTER, },
1399 { "photokano_download_frame", &Hook_photokano_download_frame, 0, REPFLAG_HOOKENTER, 0x2C },
1400 { "photokano_download_frame_2", &Hook_photokano_download_frame_2, 0, REPFLAG_HOOKENTER, },
1401 { "gakuenheaven_download_frame", &Hook_gakuenheaven_download_frame, 0, REPFLAG_HOOKENTER, },
1402 { "youkosohitsujimura_download_frame", &Hook_youkosohitsujimura_download_frame, 0, REPFLAG_HOOKENTER, 0x94 },
1403 { "zettai_hero_update_minimap_tex", &Hook_zettai_hero_update_minimap_tex, 0, REPFLAG_HOOKEXIT, },
1404 { "tonyhawkp8_upload_tutorial_frame", &Hook_tonyhawkp8_upload_tutorial_frame, 0, REPFLAG_HOOKENTER, },
1405 { "sdgundamggenerationportable_download_frame", &Hook_sdgundamggenerationportable_download_frame, 0, REPFLAG_HOOKENTER, 0x34 },
1406 { "atvoffroadfurypro_download_frame", &Hook_atvoffroadfurypro_download_frame, 0, REPFLAG_HOOKENTER, 0xA0 },
1407 { "atvoffroadfuryblazintrails_download_frame", &Hook_atvoffroadfuryblazintrails_download_frame, 0, REPFLAG_HOOKENTER, 0x80 },
1408 { "littlebustersce_download_frame", &Hook_littlebustersce_download_frame, 0, REPFLAG_HOOKENTER, },
1409 { "shinigamitoshoujo_download_frame", &Hook_shinigamitoshoujo_download_frame, 0, REPFLAG_HOOKENTER, 0xBC },
1410 { "atvoffroadfuryprodemo_download_frame", &Hook_atvoffroadfuryprodemo_download_frame, 0, REPFLAG_HOOKENTER, 0x80 },
1411 { "unendingbloodycall_download_frame", &Hook_unendingbloodycall_download_frame, 0, REPFLAG_HOOKENTER, 0x54 },
1412 { "omertachinmokunookitethelegacy_download_frame", &Hook_omertachinmokunookitethelegacy_download_frame, 0, REPFLAG_HOOKENTER, 0x88 },
1413 { "katamari_render_check", &Hook_katamari_render_check, 0, REPFLAG_HOOKENTER, 0, },
1414 { "katamari_screenshot_to_565", &Hook_katamari_screenshot_to_565, 0, REPFLAG_HOOKENTER, 0 },
1415 { "mytranwars_upload_frame", &Hook_mytranwars_upload_frame, 0, REPFLAG_HOOKENTER, 0x128 },
1416 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_a1_before, 0, REPFLAG_HOOKENTER, 0x284 },
1417 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_after, 0, REPFLAG_HOOKENTER, 0x2bc },
1418 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_a1_before, 0, REPFLAG_HOOKENTER, 0x2e8 },
1419 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_after, 0, REPFLAG_HOOKENTER, 0x320 },
1420 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_a2_before, 0, REPFLAG_HOOKENTER, 0x3b0 },
1421 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_after, 0, REPFLAG_HOOKENTER, 0x3e8 },
1422 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_a2_before, 0, REPFLAG_HOOKENTER, 0x410 },
1423 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_after, 0, REPFLAG_HOOKENTER, 0x448 },
1424 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_a1_before, 0, REPFLAG_HOOKENTER, 0x600 },
1425 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_after, 0, REPFLAG_HOOKENTER, 0x638 },
1426 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_a1_before, 0, REPFLAG_HOOKENTER, 0x664 },
1427 { "marvelalliance1_copy", &Hook_marvelalliance1_copy_after, 0, REPFLAG_HOOKENTER, 0x69c },
1428 { "starocean_clear_framebuf", &Hook_starocean_clear_framebuf_before, 0, REPFLAG_HOOKENTER, 0 },
1429 { "starocean_clear_framebuf", &Hook_starocean_clear_framebuf_after, 0, REPFLAG_HOOKEXIT, 0 },
1430 { "motorstorm_pixel_read", &Hook_motorstorm_pixel_read, 0, REPFLAG_HOOKENTER, 0 },
1431 { "worms_copy_normalize_alpha", &Hook_worms_copy_normalize_alpha, 0, REPFLAG_HOOKENTER, 0x0CC },
1432 { "openseason_data_decode", &Hook_openseason_data_decode, 0, REPFLAG_HOOKENTER, 0x2F0 },
1433 {}
1434 };
1435
1436
1437 static std::map<u32, u32> replacedInstructions;
1438 static std::unordered_map<std::string, std::vector<int> > replacementNameLookup;
1439
Replacement_Init()1440 void Replacement_Init() {
1441 for (int i = 0; i < (int)ARRAY_SIZE(entries); i++) {
1442 const auto entry = &entries[i];
1443 if (!entry->name || (entry->flags & REPFLAG_DISABLED) != 0)
1444 continue;
1445 replacementNameLookup[entry->name].push_back(i);
1446 }
1447
1448 skipGPUReplacements = 0;
1449 }
1450
Replacement_Shutdown()1451 void Replacement_Shutdown() {
1452 replacedInstructions.clear();
1453 replacementNameLookup.clear();
1454 }
1455
GetNumReplacementFuncs()1456 int GetNumReplacementFuncs() {
1457 return ARRAY_SIZE(entries);
1458 }
1459
GetReplacementFuncIndexes(u64 hash,int funcSize)1460 std::vector<int> GetReplacementFuncIndexes(u64 hash, int funcSize) {
1461 const char *name = MIPSAnalyst::LookupHash(hash, funcSize);
1462 std::vector<int> emptyResult;
1463 if (!name) {
1464 return emptyResult;
1465 }
1466
1467 auto index = replacementNameLookup.find(name);
1468 if (index != replacementNameLookup.end()) {
1469 return index->second;
1470 }
1471 return emptyResult;
1472 }
1473
GetReplacementFunc(int i)1474 const ReplacementTableEntry *GetReplacementFunc(int i) {
1475 return &entries[i];
1476 }
1477
WriteReplaceInstruction(u32 address,int index)1478 static bool WriteReplaceInstruction(u32 address, int index) {
1479 u32 prevInstr = Memory::Read_Instruction(address, false).encoding;
1480 if (MIPS_IS_REPLACEMENT(prevInstr)) {
1481 int prevIndex = prevInstr & MIPS_EMUHACK_VALUE_MASK;
1482 if (prevIndex == index) {
1483 return false;
1484 }
1485 WARN_LOG(HLE, "Replacement func changed at %08x (%d -> %d)", address, prevIndex, index);
1486 // Make sure we don't save the old replacement.
1487 prevInstr = replacedInstructions[address];
1488 }
1489
1490 if (MIPS_IS_RUNBLOCK(Memory::Read_U32(address))) {
1491 WARN_LOG(HLE, "Replacing jitted func address %08x", address);
1492 }
1493 replacedInstructions[address] = prevInstr;
1494 Memory::Write_U32(MIPS_EMUHACK_CALL_REPLACEMENT | index, address);
1495 return true;
1496 }
1497
WriteReplaceInstructions(u32 address,u64 hash,int size)1498 void WriteReplaceInstructions(u32 address, u64 hash, int size) {
1499 std::vector<int> indexes = GetReplacementFuncIndexes(hash, size);
1500 for (int index : indexes) {
1501 bool didReplace = false;
1502 auto entry = GetReplacementFunc(index);
1503 if (entry->flags & REPFLAG_HOOKEXIT) {
1504 // When hooking func exit, we search for jr ra, and replace those.
1505 for (u32 offset = 0; offset < (u32)size; offset += 4) {
1506 const u32 op = Memory::Read_Instruction(address + offset, false).encoding;
1507 if (op == MIPS_MAKE_JR_RA()) {
1508 if (WriteReplaceInstruction(address + offset, index)) {
1509 didReplace = true;
1510 }
1511 }
1512 }
1513 } else if (entry->flags & REPFLAG_HOOKENTER) {
1514 if (WriteReplaceInstruction(address + entry->hookOffset, index)) {
1515 didReplace = true;
1516 }
1517 } else {
1518 if (WriteReplaceInstruction(address, index)) {
1519 didReplace = true;
1520 }
1521 }
1522
1523 if (didReplace) {
1524 INFO_LOG(HLE, "Replaced %s at %08x with hash %016llx", entries[index].name, address, hash);
1525 }
1526 }
1527 }
1528
RestoreReplacedInstruction(u32 address)1529 void RestoreReplacedInstruction(u32 address) {
1530 const u32 curInstr = Memory::Read_U32(address);
1531 if (MIPS_IS_REPLACEMENT(curInstr)) {
1532 Memory::Write_U32(replacedInstructions[address], address);
1533 NOTICE_LOG(HLE, "Restored replaced func at %08x", address);
1534 } else {
1535 NOTICE_LOG(HLE, "Replaced func changed at %08x", address);
1536 }
1537 replacedInstructions.erase(address);
1538 }
1539
RestoreReplacedInstructions(u32 startAddr,u32 endAddr)1540 void RestoreReplacedInstructions(u32 startAddr, u32 endAddr) {
1541 if (endAddr == startAddr)
1542 return;
1543 // Need to be in order, or we'll hang.
1544 if (endAddr < startAddr)
1545 std::swap(endAddr, startAddr);
1546 const auto start = replacedInstructions.lower_bound(startAddr);
1547 const auto end = replacedInstructions.upper_bound(endAddr);
1548 int restored = 0;
1549 for (auto it = start; it != end; ++it) {
1550 const u32 addr = it->first;
1551 const u32 curInstr = Memory::Read_U32(addr);
1552 if (MIPS_IS_REPLACEMENT(curInstr)) {
1553 Memory::Write_U32(it->second, addr);
1554 ++restored;
1555 }
1556 }
1557 INFO_LOG(HLE, "Restored %d replaced funcs between %08x-%08x", restored, startAddr, endAddr);
1558 replacedInstructions.erase(start, end);
1559 }
1560
SaveAndClearReplacements()1561 std::map<u32, u32> SaveAndClearReplacements() {
1562 std::map<u32, u32> saved;
1563 for (auto it = replacedInstructions.begin(), end = replacedInstructions.end(); it != end; ++it) {
1564 const u32 addr = it->first;
1565 const u32 curInstr = Memory::Read_U32(addr);
1566 if (MIPS_IS_REPLACEMENT(curInstr)) {
1567 saved[addr] = curInstr;
1568 Memory::Write_U32(it->second, addr);
1569 }
1570 }
1571 return saved;
1572 }
1573
RestoreSavedReplacements(const std::map<u32,u32> & saved)1574 void RestoreSavedReplacements(const std::map<u32, u32> &saved) {
1575 for (auto it = saved.begin(), end = saved.end(); it != end; ++it) {
1576 const u32 addr = it->first;
1577 // Just put the replacements back.
1578 Memory::Write_U32(it->second, addr);
1579 }
1580 }
1581
GetReplacedOpAt(u32 address,u32 * op)1582 bool GetReplacedOpAt(u32 address, u32 *op) {
1583 u32 instr = Memory::Read_Opcode_JIT(address).encoding;
1584 if (MIPS_IS_REPLACEMENT(instr)) {
1585 auto iter = replacedInstructions.find(address);
1586 if (iter != replacedInstructions.end()) {
1587 *op = iter->second;
1588 return true;
1589 } else {
1590 return false;
1591 }
1592 }
1593 return false;
1594 }
1595
CanReplaceJalTo(u32 dest,const ReplacementTableEntry ** entry,u32 * funcSize)1596 bool CanReplaceJalTo(u32 dest, const ReplacementTableEntry **entry, u32 *funcSize) {
1597 MIPSOpcode op(Memory::Read_Opcode_JIT(dest));
1598 if (!MIPS_IS_REPLACEMENT(op.encoding))
1599 return false;
1600
1601 // Make sure we don't replace if there are any breakpoints inside.
1602 *funcSize = g_symbolMap->GetFunctionSize(dest);
1603 if (*funcSize == SymbolMap::INVALID_ADDRESS) {
1604 if (CBreakPoints::IsAddressBreakPoint(dest)) {
1605 return false;
1606 }
1607 *funcSize = (u32)sizeof(u32);
1608 } else {
1609 if (CBreakPoints::RangeContainsBreakPoint(dest, *funcSize)) {
1610 return false;
1611 }
1612 }
1613
1614 int index = op.encoding & MIPS_EMUHACK_VALUE_MASK;
1615 *entry = GetReplacementFunc(index);
1616 if (!*entry) {
1617 ERROR_LOG(HLE, "ReplaceJalTo: Invalid replacement op %08x at %08x", op.encoding, dest);
1618 return false;
1619 }
1620
1621 if ((*entry)->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT | REPFLAG_DISABLED)) {
1622 // If it's a hook, we can't replace the jal, we have to go inside the func.
1623 return false;
1624 }
1625 return true;
1626 }
1627