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