2013-11-30 20:57:44 +01:00
// Copyright (c) 2013- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2021-03-02 21:49:21 -08:00
# include "ppsspp_config.h"
2014-04-18 19:00:08 +02:00
# include <algorithm>
2013-12-17 23:40:27 +01:00
# include <map>
2016-09-24 14:23:17 -07:00
# include <unordered_map>
2013-12-17 23:40:27 +01:00
2022-01-30 15:49:02 -08:00
# include "Common/CommonTypes.h"
2021-02-07 09:02:28 -08:00
# include "Common/Data/Convert/SmallDataConvert.h"
2015-02-20 00:17:47 +01:00
# include "Common/Log.h"
2021-02-18 22:25:24 -08:00
# include "Common/Swap.h"
2014-05-31 18:27:02 -07:00
# include "Core/Config.h"
2022-07-02 19:14:05 +02:00
# include "Core/System.h"
2014-05-24 21:53:42 -07:00
# include "Core/Debugger/Breakpoints.h"
2021-02-02 00:08:05 -08:00
# include "Core/Debugger/MemBlockInfo.h"
2015-04-12 13:35:10 -07:00
# include "Core/Debugger/SymbolMap.h"
2014-03-15 11:22:19 -07:00
# include "Core/MemMap.h"
2013-12-17 23:40:27 +01:00
# include "Core/MIPS/JitCommon/JitCommon.h"
2014-05-30 22:45:06 -07:00
# include "Core/MIPS/MIPSCodeUtils.h"
2013-12-17 23:40:27 +01:00
# include "Core/MIPS/MIPSAnalyst.h"
2013-11-30 20:57:44 +01:00
# include "Core/HLE/ReplaceTables.h"
# include "Core/HLE/FunctionWrappers.h"
2022-07-02 19:14:05 +02:00
# include "Core/HLE/sceDisplay.h"
2013-11-30 20:57:44 +01:00
2013-12-17 23:40:27 +01:00
# include "GPU/Math3D.h"
2015-07-26 22:38:40 +02:00
# include "GPU/GPU.h"
2014-05-24 21:59:25 -07:00
# include "GPU/GPUInterface.h"
2015-07-29 11:48:20 +02:00
# include "GPU/GPUState.h"
2013-12-17 23:40:27 +01:00
2021-03-02 21:49:21 -08:00
# if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
2013-12-21 19:47:59 +01:00
# include <emmintrin.h>
# endif
2017-12-28 19:28:39 -08:00
enum class GPUReplacementSkip {
MEMSET = 1 ,
MEMCPY = 2 ,
MEMMOVE = 4 ,
} ;
static int skipGPUReplacements = 0 ;
2013-12-19 11:34:55 +01:00
// I think these have to be pretty accurate as these are libc replacements,
// but we can probably get away with approximating the VFPU vsin/vcos and vrot
// pretty roughly.
2013-12-17 23:40:27 +01:00
static int Replace_sinf ( ) {
float f = PARAMF ( 0 ) ;
RETURNF ( sinf ( f ) ) ;
return 80 ; // guess number of cycles
}
static int Replace_cosf ( ) {
float f = PARAMF ( 0 ) ;
RETURNF ( cosf ( f ) ) ;
return 80 ; // guess number of cycles
}
2013-12-22 12:50:14 +01:00
static int Replace_tanf ( ) {
float f = PARAMF ( 0 ) ;
RETURNF ( tanf ( f ) ) ;
return 80 ; // guess number of cycles
}
static int Replace_acosf ( ) {
float f = PARAMF ( 0 ) ;
RETURNF ( acosf ( f ) ) ;
return 80 ; // guess number of cycles
}
static int Replace_asinf ( ) {
float f = PARAMF ( 0 ) ;
RETURNF ( asinf ( f ) ) ;
return 80 ; // guess number of cycles
}
static int Replace_atanf ( ) {
float f = PARAMF ( 0 ) ;
RETURNF ( atanf ( f ) ) ;
return 80 ; // guess number of cycles
}
2013-12-18 10:35:16 +01:00
static int Replace_sqrtf ( ) {
float f = PARAMF ( 0 ) ;
RETURNF ( sqrtf ( f ) ) ;
return 80 ; // guess number of cycles
}
static int Replace_atan2f ( ) {
float f1 = PARAMF ( 0 ) ;
float f2 = PARAMF ( 1 ) ;
RETURNF ( atan2f ( f1 , f2 ) ) ;
return 120 ; // guess number of cycles
}
2013-12-18 16:27:23 +01:00
2013-12-21 12:36:30 +01:00
static int Replace_floorf ( ) {
float f1 = PARAMF ( 0 ) ;
RETURNF ( floorf ( f1 ) ) ;
return 30 ; // guess number of cycles
}
static int Replace_ceilf ( ) {
float f1 = PARAMF ( 0 ) ;
RETURNF ( ceilf ( f1 ) ) ;
return 30 ; // guess number of cycles
}
2013-12-17 23:40:27 +01:00
// Should probably do JIT versions of this, possibly ones that only delegate
// large copies to a C function.
static int Replace_memcpy ( ) {
u32 destPtr = PARAM ( 0 ) ;
2013-12-19 00:15:03 -08:00
u32 srcPtr = PARAM ( 1 ) ;
2013-12-17 23:40:27 +01:00
u32 bytes = PARAM ( 2 ) ;
2014-05-25 20:05:28 -07:00
bool skip = false ;
2015-01-11 00:35:43 +01:00
if ( ! bytes ) {
RETURN ( destPtr ) ;
2015-01-10 01:52:20 +01:00
return 10 ;
2015-01-11 00:35:43 +01:00
}
2014-07-05 13:23:33 -07:00
// Some games use memcpy on executable code. We need to flush emuhack ops.
currentMIPS - > InvalidateICache ( srcPtr , bytes ) ;
2017-12-28 19:28:39 -08:00
if ( ( skipGPUReplacements & ( int ) GPUReplacementSkip : : MEMCPY ) = = 0 ) {
if ( Memory : : IsVRAMAddress ( destPtr ) | | Memory : : IsVRAMAddress ( srcPtr ) ) {
skip = gpu - > PerformMemoryCopy ( destPtr , srcPtr , bytes ) ;
}
2014-05-25 20:05:28 -07:00
}
2014-08-03 13:04:00 -07:00
if ( ! skip & & bytes ! = 0 ) {
2022-07-20 12:40:22 +02:00
u8 * dst = Memory : : GetPointerWrite ( destPtr ) ;
2014-08-03 13:04:00 -07:00
const u8 * src = Memory : : GetPointer ( srcPtr ) ;
2014-05-31 18:26:24 -07:00
2014-08-03 13:04:00 -07:00
if ( ! dst | | ! src ) {
// Already logged.
} else if ( std : : min ( destPtr , srcPtr ) + bytes > std : : max ( destPtr , srcPtr ) ) {
2014-05-31 18:26:24 -07:00
// Overlap. Star Ocean breaks if it's not handled in 16 bytes blocks.
const u32 blocks = bytes & ~ 0x0f ;
for ( u32 offset = 0 ; offset < blocks ; offset + = 0x10 ) {
memcpy ( dst + offset , src + offset , 0x10 ) ;
}
for ( u32 offset = blocks ; offset < bytes ; + + offset ) {
dst [ offset ] = src [ offset ] ;
}
} else {
memmove ( dst , src , bytes ) ;
}
2013-12-17 23:40:27 +01:00
}
RETURN ( destPtr ) ;
2018-05-01 18:11:57 -07:00
2022-02-06 09:28:48 -08:00
if ( MemBlockInfoDetailed ( bytes ) ) {
2022-09-01 11:55:14 +02:00
const std : : string tag = GetMemWriteTagAt ( " ReplaceMemcpy/ " , srcPtr , bytes ) ;
2022-02-06 09:28:48 -08:00
NotifyMemInfo ( MemBlockFlags : : READ , srcPtr , bytes , tag . c_str ( ) , tag . size ( ) ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , destPtr , bytes , tag . c_str ( ) , tag . size ( ) ) ;
// It's pretty common that games will copy video data.
if ( tag = = " ReplaceMemcpy/VideoDecode " | | tag = = " ReplaceMemcpy/VideoDecodeRange " ) {
if ( bytes = = 512 * 272 * 4 ) {
gpu - > NotifyVideoUpload ( destPtr , bytes , 512 , GE_FORMAT_8888 ) ;
}
2021-09-19 07:51:33 -07:00
}
}
2013-12-17 23:40:27 +01:00
return 10 + bytes / 4 ; // approximation
}
2015-02-19 11:14:23 +01:00
static int Replace_memcpy_jak ( ) {
u32 destPtr = PARAM ( 0 ) ;
u32 srcPtr = PARAM ( 1 ) ;
u32 bytes = PARAM ( 2 ) ;
bool skip = false ;
2015-02-20 00:17:47 +01:00
if ( bytes = = 0 ) {
2015-02-19 11:14:23 +01:00
RETURN ( destPtr ) ;
2015-02-20 00:17:47 +01:00
return 5 ;
2015-02-19 11:14:23 +01:00
}
currentMIPS - > InvalidateICache ( srcPtr , bytes ) ;
2017-12-28 19:28:39 -08:00
if ( ( skipGPUReplacements & ( int ) GPUReplacementSkip : : MEMCPY ) = = 0 ) {
if ( Memory : : IsVRAMAddress ( destPtr ) | | Memory : : IsVRAMAddress ( srcPtr ) ) {
skip = gpu - > PerformMemoryCopy ( destPtr , srcPtr , bytes ) ;
}
2015-02-19 11:14:23 +01:00
}
if ( ! skip & & bytes ! = 0 ) {
2022-07-20 12:40:22 +02:00
u8 * dst = Memory : : GetPointerWrite ( destPtr ) ;
2015-02-19 11:14:23 +01:00
const u8 * src = Memory : : GetPointer ( srcPtr ) ;
if ( ! dst | | ! src ) {
2015-02-20 00:17:47 +01:00
} else {
2015-02-19 11:14:23 +01:00
// Jak style overlap.
2015-02-21 15:48:09 -08:00
for ( u32 i = 0 ; i < bytes ; i + + ) {
2015-02-19 11:14:23 +01:00
dst [ i ] = src [ i ] ;
}
}
}
// Jak relies on more registers coming out right than the ABI specifies.
// See the disassembly of the function for the explanations for these...
currentMIPS - > r [ MIPS_REG_T0 ] = 0 ;
currentMIPS - > r [ MIPS_REG_A0 ] = - 1 ;
2015-02-21 13:07:13 +01:00
currentMIPS - > r [ MIPS_REG_A2 ] = 0 ;
2015-02-19 11:14:23 +01:00
currentMIPS - > r [ MIPS_REG_A3 ] = destPtr + bytes ;
2015-02-20 00:17:47 +01:00
RETURN ( destPtr ) ;
2018-05-01 18:11:57 -07:00
2022-02-06 09:28:48 -08:00
if ( MemBlockInfoDetailed ( bytes ) ) {
2022-09-01 11:55:14 +02:00
const std : : string tag = GetMemWriteTagAt ( " ReplaceMemcpy/ " , srcPtr , bytes ) ;
2022-02-06 09:28:48 -08:00
NotifyMemInfo ( MemBlockFlags : : READ , srcPtr , bytes , tag . c_str ( ) , tag . size ( ) ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , destPtr , bytes , tag . c_str ( ) , tag . size ( ) ) ;
2018-05-01 18:11:57 -07:00
2022-02-06 09:28:48 -08:00
// It's pretty common that games will copy video data.
if ( tag = = " ReplaceMemcpy/VideoDecode " | | tag = = " ReplaceMemcpy/VideoDecodeRange " ) {
if ( bytes = = 512 * 272 * 4 ) {
gpu - > NotifyVideoUpload ( destPtr , bytes , 512 , GE_FORMAT_8888 ) ;
}
2021-09-19 07:51:33 -07:00
}
}
2015-02-20 00:17:47 +01:00
return 5 + bytes * 8 + 2 ; // approximation. This is a slow memcpy - a byte copy loop..
2015-02-19 11:14:23 +01:00
}
2013-12-27 21:39:05 -08:00
static int Replace_memcpy16 ( ) {
u32 destPtr = PARAM ( 0 ) ;
u32 srcPtr = PARAM ( 1 ) ;
u32 bytes = PARAM ( 2 ) * 16 ;
2014-05-25 20:05:28 -07:00
bool skip = false ;
2014-07-05 13:23:33 -07:00
// Some games use memcpy on executable code. We need to flush emuhack ops.
currentMIPS - > InvalidateICache ( srcPtr , bytes ) ;
2017-12-28 19:28:39 -08:00
if ( ( skipGPUReplacements & ( int ) GPUReplacementSkip : : MEMCPY ) = = 0 ) {
if ( Memory : : IsVRAMAddress ( destPtr ) | | Memory : : IsVRAMAddress ( srcPtr ) ) {
skip = gpu - > PerformMemoryCopy ( destPtr , srcPtr , bytes ) ;
}
2014-05-25 20:05:28 -07:00
}
if ( ! skip & & bytes ! = 0 ) {
2022-07-20 12:40:22 +02:00
u8 * dst = Memory : : GetPointerWrite ( destPtr ) ;
2014-08-03 13:04:00 -07:00
const u8 * src = Memory : : GetPointer ( srcPtr ) ;
if ( dst & & src ) {
memmove ( dst , src , bytes ) ;
}
2013-12-27 21:39:05 -08:00
}
RETURN ( destPtr ) ;
2018-05-01 18:11:57 -07:00
2022-02-06 09:28:48 -08:00
if ( MemBlockInfoDetailed ( bytes ) ) {
2022-09-01 11:55:14 +02:00
const std : : string tag = GetMemWriteTagAt ( " ReplaceMemcpy16/ " , srcPtr , bytes ) ;
2022-02-06 09:28:48 -08:00
NotifyMemInfo ( MemBlockFlags : : READ , srcPtr , bytes , tag . c_str ( ) , tag . size ( ) ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , destPtr , bytes , tag . c_str ( ) , tag . size ( ) ) ;
}
2018-05-01 18:11:57 -07:00
2013-12-27 21:39:05 -08:00
return 10 + bytes / 4 ; // approximation
}
2014-05-26 16:49:32 -07:00
static int Replace_memcpy_swizzled ( ) {
u32 destPtr = PARAM ( 0 ) ;
u32 srcPtr = PARAM ( 1 ) ;
u32 pitch = PARAM ( 2 ) ;
u32 h = PARAM ( 4 ) ;
2017-12-28 19:28:39 -08:00
if ( ( skipGPUReplacements & ( int ) GPUReplacementSkip : : MEMCPY ) = = 0 ) {
if ( Memory : : IsVRAMAddress ( srcPtr ) ) {
gpu - > PerformMemoryDownload ( srcPtr , pitch * h ) ;
}
2014-05-26 16:49:32 -07:00
}
2022-07-20 12:40:22 +02:00
u8 * dstp = Memory : : GetPointerWrite ( destPtr ) ;
2014-08-03 13:04:00 -07:00
const u8 * srcp = Memory : : GetPointer ( srcPtr ) ;
if ( dstp & & srcp ) {
const u8 * ysrcp = srcp ;
for ( u32 y = 0 ; y < h ; y + = 8 ) {
const u8 * xsrcp = ysrcp ;
for ( u32 x = 0 ; x < pitch ; x + = 16 ) {
const u8 * src = xsrcp ;
for ( int n = 0 ; n < 8 ; + + n ) {
memcpy ( dstp , src , 16 ) ;
src + = pitch ;
dstp + = 16 ;
}
xsrcp + = 16 ;
2014-05-26 16:49:32 -07:00
}
2014-08-03 13:04:00 -07:00
ysrcp + = 8 * pitch ;
2014-05-26 16:49:32 -07:00
}
}
RETURN ( 0 ) ;
2018-05-01 18:11:57 -07:00
2022-02-06 09:28:48 -08:00
if ( MemBlockInfoDetailed ( pitch * h ) ) {
2022-09-01 11:55:14 +02:00
const std : : string tag = GetMemWriteTagAt ( " ReplaceMemcpySwizzle/ " , srcPtr , pitch * h ) ;
2022-02-06 09:28:48 -08:00
NotifyMemInfo ( MemBlockFlags : : READ , srcPtr , pitch * h , tag . c_str ( ) , tag . size ( ) ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , destPtr , pitch * h , tag . c_str ( ) , tag . size ( ) ) ;
}
2018-05-01 18:11:57 -07:00
2014-05-26 16:49:32 -07:00
return 10 + ( pitch * h ) / 4 ; // approximation
}
2013-12-18 10:35:16 +01:00
static int Replace_memmove ( ) {
u32 destPtr = PARAM ( 0 ) ;
2013-12-19 00:15:03 -08:00
u32 srcPtr = PARAM ( 1 ) ;
2013-12-18 10:35:16 +01:00
u32 bytes = PARAM ( 2 ) ;
2014-05-25 20:05:28 -07:00
bool skip = false ;
2014-07-05 13:23:33 -07:00
// Some games use memcpy on executable code. We need to flush emuhack ops.
2017-12-28 19:28:39 -08:00
if ( ( skipGPUReplacements & ( int ) GPUReplacementSkip : : MEMMOVE ) = = 0 ) {
currentMIPS - > InvalidateICache ( srcPtr , bytes ) ;
if ( Memory : : IsVRAMAddress ( destPtr ) | | Memory : : IsVRAMAddress ( srcPtr ) ) {
skip = gpu - > PerformMemoryCopy ( destPtr , srcPtr , bytes ) ;
}
2014-05-25 20:05:28 -07:00
}
if ( ! skip & & bytes ! = 0 ) {
2022-07-20 12:40:22 +02:00
u8 * dst = Memory : : GetPointerWrite ( destPtr ) ;
2014-08-03 13:04:00 -07:00
const u8 * src = Memory : : GetPointer ( srcPtr ) ;
if ( dst & & src ) {
memmove ( dst , src , bytes ) ;
}
2013-12-18 10:35:16 +01:00
}
RETURN ( destPtr ) ;
2018-05-01 18:11:57 -07:00
2022-02-06 09:28:48 -08:00
if ( MemBlockInfoDetailed ( bytes ) ) {
2022-09-01 11:55:14 +02:00
const std : : string tag = GetMemWriteTagAt ( " ReplaceMemmove/ " , srcPtr , bytes ) ;
2022-02-06 09:28:48 -08:00
NotifyMemInfo ( MemBlockFlags : : READ , srcPtr , bytes , tag . c_str ( ) , tag . size ( ) ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , destPtr , bytes , tag . c_str ( ) , tag . size ( ) ) ;
}
2018-05-01 18:11:57 -07:00
2013-12-18 10:35:16 +01:00
return 10 + bytes / 4 ; // approximation
}
2013-12-17 23:40:27 +01:00
static int Replace_memset ( ) {
u32 destPtr = PARAM ( 0 ) ;
u8 value = PARAM ( 1 ) ;
u32 bytes = PARAM ( 2 ) ;
2014-05-27 01:14:29 -07:00
bool skip = false ;
2017-12-28 19:28:39 -08:00
if ( Memory : : IsVRAMAddress ( destPtr ) & & ( skipGPUReplacements & ( int ) GPUReplacementSkip : : MEMSET ) = = 0 ) {
2014-05-27 01:14:29 -07:00
skip = gpu - > PerformMemorySet ( destPtr , value , bytes ) ;
}
2014-08-03 13:04:00 -07:00
if ( ! skip & & bytes ! = 0 ) {
2022-07-20 12:40:22 +02:00
u8 * dst = Memory : : GetPointerWrite ( destPtr ) ;
2014-08-03 13:04:00 -07:00
if ( dst ) {
memset ( dst , value , bytes ) ;
}
2014-05-25 20:05:28 -07:00
}
2013-12-17 23:40:27 +01:00
RETURN ( destPtr ) ;
2018-05-01 18:11:57 -07:00
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , destPtr , bytes , " ReplaceMemset " ) ;
2018-05-01 18:11:57 -07:00
2013-12-17 23:40:27 +01:00
return 10 + bytes / 4 ; // approximation
}
2015-02-20 00:17:47 +01:00
static int Replace_memset_jak ( ) {
u32 destPtr = PARAM ( 0 ) ;
u8 value = PARAM ( 1 ) ;
u32 bytes = PARAM ( 2 ) ;
if ( bytes = = 0 ) {
RETURN ( destPtr ) ;
return 5 ;
}
bool skip = false ;
2017-12-28 19:28:39 -08:00
if ( Memory : : IsVRAMAddress ( destPtr ) & & ( skipGPUReplacements & ( int ) GPUReplacementSkip : : MEMSET ) = = 0 ) {
2015-02-20 00:17:47 +01:00
skip = gpu - > PerformMemorySet ( destPtr , value , bytes ) ;
}
if ( ! skip & & bytes ! = 0 ) {
2022-07-20 12:40:22 +02:00
u8 * dst = Memory : : GetPointerWrite ( destPtr ) ;
2015-02-20 00:17:47 +01:00
if ( dst ) {
memset ( dst , value , bytes ) ;
}
}
currentMIPS - > r [ MIPS_REG_T0 ] = destPtr + bytes ;
currentMIPS - > r [ MIPS_REG_A2 ] = - 1 ;
currentMIPS - > r [ MIPS_REG_A3 ] = - 1 ;
RETURN ( destPtr ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , destPtr , bytes , " ReplaceMemset " ) ;
2018-05-01 18:11:57 -07:00
2015-02-21 13:07:13 +01:00
return 5 + bytes * 6 + 2 ; // approximation (hm, inspecting the disasm this should be 5 + 6 * bytes + 2, but this is what works..)
2015-02-20 00:17:47 +01:00
}
2013-12-17 23:40:27 +01:00
static int Replace_strlen ( ) {
u32 srcPtr = PARAM ( 0 ) ;
2014-08-03 13:04:00 -07:00
const char * src = ( const char * ) Memory : : GetPointer ( srcPtr ) ;
u32 len = src ? ( u32 ) strlen ( src ) : 0UL ;
2013-12-17 23:40:27 +01:00
RETURN ( len ) ;
2014-06-22 23:18:06 -07:00
return 7 + len * 4 ; // approximation
2013-12-17 23:40:27 +01:00
}
2013-12-18 10:35:16 +01:00
static int Replace_strcpy ( ) {
u32 destPtr = PARAM ( 0 ) ;
2014-08-03 13:04:00 -07:00
char * dst = ( char * ) Memory : : GetPointer ( destPtr ) ;
const char * src = ( const char * ) Memory : : GetPointer ( PARAM ( 1 ) ) ;
if ( dst & & src ) {
strcpy ( dst , src ) ;
}
2013-12-19 11:45:39 +01:00
RETURN ( destPtr ) ;
return 10 ; // approximation
}
static int Replace_strncpy ( ) {
u32 destPtr = PARAM ( 0 ) ;
2014-08-03 13:04:00 -07:00
char * dst = ( char * ) Memory : : GetPointer ( destPtr ) ;
const char * src = ( const char * ) Memory : : GetPointer ( PARAM ( 1 ) ) ;
2013-12-19 11:45:39 +01:00
u32 bytes = PARAM ( 2 ) ;
2014-08-03 13:04:00 -07:00
if ( dst & & src & & bytes ! = 0 ) {
strncpy ( dst , src , bytes ) ;
}
2013-12-18 10:35:16 +01:00
RETURN ( destPtr ) ;
2013-12-18 11:22:53 +01:00
return 10 ; // approximation
2013-12-18 10:35:16 +01:00
}
static int Replace_strcmp ( ) {
2014-08-03 13:04:00 -07:00
const char * a = ( const char * ) Memory : : GetPointer ( PARAM ( 0 ) ) ;
const char * b = ( const char * ) Memory : : GetPointer ( PARAM ( 1 ) ) ;
if ( a & & b ) {
RETURN ( strcmp ( a , b ) ) ;
} else {
RETURN ( 0 ) ;
}
2013-12-18 10:35:16 +01:00
return 10 ; // approximation
}
2013-12-18 11:22:53 +01:00
static int Replace_strncmp ( ) {
2014-08-03 13:04:00 -07:00
const char * a = ( const char * ) Memory : : GetPointer ( PARAM ( 0 ) ) ;
const char * b = ( const char * ) Memory : : GetPointer ( PARAM ( 1 ) ) ;
2013-12-18 11:22:53 +01:00
u32 bytes = PARAM ( 2 ) ;
2014-08-03 13:04:00 -07:00
if ( a & & b & & bytes ! = 0 ) {
RETURN ( strncmp ( a , b , bytes ) ) ;
} else {
RETURN ( 0 ) ;
}
2013-12-18 11:22:53 +01:00
return 10 + bytes / 4 ; // approximation
}
2014-04-28 08:01:13 -07:00
static int Replace_fabsf ( ) {
RETURNF ( fabsf ( PARAMF ( 0 ) ) ) ;
return 4 ;
}
2013-12-17 23:40:27 +01:00
static int Replace_vmmul_q_transp ( ) {
2021-02-18 22:27:48 -08:00
float_le * out = ( float_le * ) Memory : : GetPointer ( PARAM ( 0 ) ) ;
const float_le * a = ( const float_le * ) Memory : : GetPointer ( PARAM ( 1 ) ) ;
const float_le * b = ( const float_le * ) Memory : : GetPointer ( PARAM ( 2 ) ) ;
2013-12-17 23:40:27 +01:00
// TODO: Actually use an optimized matrix multiply here...
2014-08-03 13:04:00 -07:00
if ( out & & b & & a ) {
2021-02-18 22:27:48 -08:00
# ifdef COMMON_BIG_ENDIAN
float outn [ 16 ] , an [ 16 ] , bn [ 16 ] ;
for ( int i = 0 ; i < 16 ; + + i ) {
an [ i ] = a [ i ] ;
bn [ i ] = b [ i ] ;
}
Matrix4ByMatrix4 ( outn , bn , an ) ;
for ( int i = 0 ; i < 16 ; + + i ) {
out [ i ] = outn [ i ] ;
}
# else
2014-08-03 13:04:00 -07:00
Matrix4ByMatrix4 ( out , b , a ) ;
2021-02-18 22:27:48 -08:00
# endif
2014-08-03 13:04:00 -07:00
}
2013-12-17 23:40:27 +01:00
return 16 ;
2013-11-30 20:57:44 +01:00
}
2013-12-21 12:36:30 +01:00
// a0 = pointer to destination address
// a1 = matrix
// a2 = source address
static int Replace_gta_dl_write_matrix ( ) {
2021-02-18 22:25:24 -08:00
u32_le * ptr = ( u32_le * ) Memory : : GetPointer ( PARAM ( 0 ) ) ;
u32_le * src = ( u32_le * ) Memory : : GetPointer ( PARAM ( 2 ) ) ;
2013-12-21 12:36:30 +01:00
u32 matrix = PARAM ( 1 ) < < 24 ;
2014-12-10 23:11:55 -05:00
if ( ! ptr | | ! src ) {
RETURN ( 0 ) ;
return 38 ;
}
2021-02-18 22:25:24 -08:00
u32_le * dest = ( u32_le * ) Memory : : GetPointer ( ptr [ 0 ] ) ;
2014-12-10 23:11:55 -05:00
if ( ! dest ) {
RETURN ( 0 ) ;
return 38 ;
}
2021-03-02 21:49:21 -08:00
# if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
2014-12-10 23:11:55 -05:00
__m128i topBytes = _mm_set1_epi32 ( matrix ) ;
__m128i m0 = _mm_loadu_si128 ( ( const __m128i * ) src ) ;
__m128i m1 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 4 ) ) ;
__m128i m2 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 8 ) ) ;
__m128i m3 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 12 ) ) ;
m0 = _mm_or_si128 ( _mm_srli_epi32 ( m0 , 8 ) , topBytes ) ;
m1 = _mm_or_si128 ( _mm_srli_epi32 ( m1 , 8 ) , topBytes ) ;
m2 = _mm_or_si128 ( _mm_srli_epi32 ( m2 , 8 ) , topBytes ) ;
m3 = _mm_or_si128 ( _mm_srli_epi32 ( m3 , 8 ) , topBytes ) ;
// These three stores overlap by a word, due to the offsets.
_mm_storeu_si128 ( ( __m128i * ) dest , m0 ) ;
_mm_storeu_si128 ( ( __m128i * ) ( dest + 3 ) , m1 ) ;
_mm_storeu_si128 ( ( __m128i * ) ( dest + 6 ) , m2 ) ;
// Store the last one in parts to not overwrite forwards (probably mostly risk free though)
_mm_storel_epi64 ( ( __m128i * ) ( dest + 9 ) , m3 ) ;
m3 = _mm_srli_si128 ( m3 , 8 ) ;
_mm_store_ss ( ( float * ) ( dest + 11 ) , _mm_castsi128_ps ( m3 ) ) ;
2013-12-21 12:36:30 +01:00
# else
2014-12-10 23:11:55 -05:00
// Bit tricky to SIMD (note the offsets) but should be doable if not perfect
dest [ 0 ] = matrix | ( src [ 0 ] > > 8 ) ;
dest [ 1 ] = matrix | ( src [ 1 ] > > 8 ) ;
dest [ 2 ] = matrix | ( src [ 2 ] > > 8 ) ;
dest [ 3 ] = matrix | ( src [ 4 ] > > 8 ) ;
dest [ 4 ] = matrix | ( src [ 5 ] > > 8 ) ;
dest [ 5 ] = matrix | ( src [ 6 ] > > 8 ) ;
dest [ 6 ] = matrix | ( src [ 8 ] > > 8 ) ;
dest [ 7 ] = matrix | ( src [ 9 ] > > 8 ) ;
dest [ 8 ] = matrix | ( src [ 10 ] > > 8 ) ;
dest [ 9 ] = matrix | ( src [ 12 ] > > 8 ) ;
dest [ 10 ] = matrix | ( src [ 13 ] > > 8 ) ;
dest [ 11 ] = matrix | ( src [ 14 ] > > 8 ) ;
2013-12-21 12:36:30 +01:00
# endif
2014-12-10 23:11:55 -05:00
( * ptr ) + = 0x30 ;
2014-08-03 13:04:00 -07:00
2013-12-21 12:36:30 +01:00
RETURN ( 0 ) ;
return 38 ;
}
2013-12-20 15:37:37 +01:00
// TODO: Inline into a few NEON or SSE instructions - especially if a1 is a known immediate!
// Anyway, not sure if worth it. There's not that many matrices written per frame normally.
static int Replace_dl_write_matrix ( ) {
2021-02-18 22:25:24 -08:00
u32_le * dlStruct = ( u32_le * ) Memory : : GetPointer ( PARAM ( 0 ) ) ;
u32_le * src = ( u32_le * ) Memory : : GetPointer ( PARAM ( 2 ) ) ;
2014-08-03 13:04:00 -07:00
2014-12-10 23:11:55 -05:00
if ( ! dlStruct | | ! src ) {
RETURN ( 0 ) ;
return 60 ;
}
2021-02-18 22:25:24 -08:00
u32_le * dest = ( u32_le * ) Memory : : GetPointer ( dlStruct [ 2 ] ) ;
2014-12-10 23:11:55 -05:00
if ( ! dest ) {
2014-08-03 13:04:00 -07:00
RETURN ( 0 ) ;
return 60 ;
}
2013-12-20 15:37:37 +01:00
2016-03-20 14:17:51 -07:00
u32 matrix = 0 ;
2013-12-20 15:37:37 +01:00
int count = 12 ;
switch ( PARAM ( 1 ) ) {
case 3 :
matrix = 0x40000000 ; // tex mtx
break ;
case 2 :
matrix = 0x3A000000 ;
break ;
case 1 :
matrix = 0x3C000000 ;
break ;
case 0 :
matrix = 0x3E000000 ;
count = 16 ;
break ;
}
2013-12-21 12:36:30 +01:00
* dest + + = matrix ;
2013-12-20 15:37:37 +01:00
matrix + = 0x01000000 ;
if ( count = = 16 ) {
2013-12-21 12:36:30 +01:00
// Ultra SIMD friendly! These intrinsics generate pretty much perfect code,
// no point in hand rolling.
2021-03-02 21:49:21 -08:00
# if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
2013-12-21 12:36:30 +01:00
__m128i topBytes = _mm_set1_epi32 ( matrix ) ;
__m128i m0 = _mm_loadu_si128 ( ( const __m128i * ) src ) ;
__m128i m1 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 4 ) ) ;
__m128i m2 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 8 ) ) ;
__m128i m3 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 12 ) ) ;
m0 = _mm_or_si128 ( _mm_srli_epi32 ( m0 , 8 ) , topBytes ) ;
m1 = _mm_or_si128 ( _mm_srli_epi32 ( m1 , 8 ) , topBytes ) ;
m2 = _mm_or_si128 ( _mm_srli_epi32 ( m2 , 8 ) , topBytes ) ;
m3 = _mm_or_si128 ( _mm_srli_epi32 ( m3 , 8 ) , topBytes ) ;
_mm_storeu_si128 ( ( __m128i * ) dest , m0 ) ;
_mm_storeu_si128 ( ( __m128i * ) ( dest + 4 ) , m1 ) ;
_mm_storeu_si128 ( ( __m128i * ) ( dest + 8 ) , m2 ) ;
_mm_storeu_si128 ( ( __m128i * ) ( dest + 12 ) , m3 ) ;
# else
#if 0
//TODO: Finish NEON, make conditional somehow
uint32x4_t topBytes = vdupq_n_u32 ( matrix ) ;
uint32x4_t m0 = vld1q_u32 ( dataPtr ) ;
uint32x4_t m1 = vld1q_u32 ( dataPtr + 4 ) ;
uint32x4_t m2 = vld1q_u32 ( dataPtr + 8 ) ;
uint32x4_t m3 = vld1q_u32 ( dataPtr + 12 ) ;
m0 = vorr_u32 ( vsri_n_u32 ( m0 , 8 ) , topBytes ) ; // TODO: look into VSRI
m1 = vorr_u32 ( vshr_n_u32 ( m1 , 8 ) , topBytes ) ;
m2 = vorr_u32 ( vshr_n_u32 ( m2 , 8 ) , topBytes ) ;
m3 = vorr_u32 ( vshr_n_u32 ( m3 , 8 ) , topBytes ) ;
vst1q_u32 ( dlPtr , m0 ) ;
vst1q_u32 ( dlPtr + 4 , m1 ) ;
vst1q_u32 ( dlPtr + 8 , m2 ) ;
vst1q_u32 ( dlPtr + 12 , m3 ) ;
# endif
2013-12-20 15:37:37 +01:00
for ( int i = 0 ; i < count ; i + + ) {
2013-12-21 12:36:30 +01:00
dest [ i ] = matrix | ( src [ i ] > > 8 ) ;
2013-12-20 15:37:37 +01:00
}
2013-12-21 12:36:30 +01:00
# endif
2013-12-20 15:37:37 +01:00
} else {
2021-03-02 21:49:21 -08:00
# if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
2013-12-21 12:36:30 +01:00
__m128i topBytes = _mm_set1_epi32 ( matrix ) ;
__m128i m0 = _mm_loadu_si128 ( ( const __m128i * ) src ) ;
__m128i m1 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 4 ) ) ;
__m128i m2 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 8 ) ) ;
__m128i m3 = _mm_loadu_si128 ( ( const __m128i * ) ( src + 12 ) ) ;
m0 = _mm_or_si128 ( _mm_srli_epi32 ( m0 , 8 ) , topBytes ) ;
m1 = _mm_or_si128 ( _mm_srli_epi32 ( m1 , 8 ) , topBytes ) ;
m2 = _mm_or_si128 ( _mm_srli_epi32 ( m2 , 8 ) , topBytes ) ;
m3 = _mm_or_si128 ( _mm_srli_epi32 ( m3 , 8 ) , topBytes ) ;
// These three stores overlap by a word, due to the offsets.
_mm_storeu_si128 ( ( __m128i * ) dest , m0 ) ;
_mm_storeu_si128 ( ( __m128i * ) ( dest + 3 ) , m1 ) ;
_mm_storeu_si128 ( ( __m128i * ) ( dest + 6 ) , m2 ) ;
// Store the last one in parts to not overwrite forwards (probably mostly risk free though)
_mm_storel_epi64 ( ( __m128i * ) ( dest + 9 ) , m3 ) ;
m3 = _mm_srli_si128 ( m3 , 8 ) ;
_mm_store_ss ( ( float * ) ( dest + 11 ) , _mm_castsi128_ps ( m3 ) ) ;
# else
// Bit tricky to SIMD (note the offsets) but should be doable if not perfect
dest [ 0 ] = matrix | ( src [ 0 ] > > 8 ) ;
dest [ 1 ] = matrix | ( src [ 1 ] > > 8 ) ;
dest [ 2 ] = matrix | ( src [ 2 ] > > 8 ) ;
dest [ 3 ] = matrix | ( src [ 4 ] > > 8 ) ;
dest [ 4 ] = matrix | ( src [ 5 ] > > 8 ) ;
dest [ 5 ] = matrix | ( src [ 6 ] > > 8 ) ;
dest [ 6 ] = matrix | ( src [ 8 ] > > 8 ) ;
dest [ 7 ] = matrix | ( src [ 9 ] > > 8 ) ;
dest [ 8 ] = matrix | ( src [ 10 ] > > 8 ) ;
dest [ 9 ] = matrix | ( src [ 12 ] > > 8 ) ;
dest [ 10 ] = matrix | ( src [ 13 ] > > 8 ) ;
dest [ 11 ] = matrix | ( src [ 14 ] > > 8 ) ;
# endif
2013-12-20 15:37:37 +01:00
}
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : READ , PARAM ( 2 ) , count * sizeof ( float ) , " ReplaceDLWriteMatrix " ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , PARAM ( 0 ) + 2 * sizeof ( u32 ) , sizeof ( u32 ) , " ReplaceDLWriteMatrix " ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , dlStruct [ 2 ] , ( count + 1 ) * sizeof ( u32 ) , " ReplaceDLWriteMatrix " ) ;
2014-05-24 21:53:42 -07:00
2013-12-20 15:37:37 +01:00
dlStruct [ 2 ] + = ( 1 + count ) * 4 ;
RETURN ( dlStruct [ 2 ] ) ;
return 60 ;
}
2014-05-30 22:49:16 -07:00
static bool GetMIPSStaticAddress ( u32 & addr , s32 lui_offset , s32 lw_offset ) {
2014-05-30 23:28:21 -07:00
const MIPSOpcode upper = Memory : : Read_Instruction ( currentMIPS - > pc + lui_offset , true ) ;
2014-05-30 22:49:16 -07:00
if ( upper ! = MIPS_MAKE_LUI ( MIPS_GET_RT ( upper ) , upper & 0xffff ) ) {
return false ;
}
2014-05-30 23:28:21 -07:00
const MIPSOpcode lower = Memory : : Read_Instruction ( currentMIPS - > pc + lw_offset , true ) ;
2014-05-30 22:49:16 -07:00
if ( lower ! = MIPS_MAKE_LW ( MIPS_GET_RT ( lower ) , MIPS_GET_RS ( lower ) , lower & 0xffff ) ) {
2016-05-20 22:28:26 -07:00
if ( lower ! = MIPS_MAKE_ORI ( MIPS_GET_RT ( lower ) , MIPS_GET_RS ( lower ) , lower & 0xffff ) ) {
return false ;
}
2014-05-30 22:49:16 -07:00
}
addr = ( ( upper & 0xffff ) < < 16 ) + ( s16 ) ( lower & 0xffff ) ;
return true ;
}
2017-12-28 19:28:39 -08:00
static bool GetMIPSGPAddress ( u32 & addr , s32 offset ) {
const MIPSOpcode loadOp = Memory : : Read_Instruction ( currentMIPS - > pc + offset , true ) ;
if ( MIPS_GET_RS ( loadOp ) = = MIPS_REG_GP ) {
s16 gpoff = ( s16 ) ( u16 ) ( loadOp & 0x0000FFFF ) ;
addr = currentMIPS - > r [ MIPS_REG_GP ] + gpoff ;
return true ;
}
return false ;
}
2014-05-30 22:49:16 -07:00
static int Hook_godseaterburst_blit_texture ( ) {
u32 texaddr ;
// Only if there's no texture.
if ( ! GetMIPSStaticAddress ( texaddr , 0x000c , 0x0030 ) ) {
return 0 ;
}
u32 fb_infoaddr ;
if ( Memory : : Read_U32 ( texaddr ) ! = 0 | | ! GetMIPSStaticAddress ( fb_infoaddr , 0x01d0 , 0x01d4 ) ) {
return 0 ;
}
const u32 fb_info = Memory : : Read_U32 ( fb_infoaddr ) ;
const u32 fb_address = Memory : : Read_U32 ( fb_info ) ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
2014-06-10 22:56:45 -07:00
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " godseaterburst_blit_texture " ) ;
2014-05-30 22:49:16 -07:00
}
return 0 ;
}
2022-10-09 00:52:35 -07:00
static int Hook_godseaterburst_depthmask_5551 ( ) {
// This function copies the 5551 framebuffer to a temporary, generating alpha based on depth.
// Depth is optional, in which case all pixels get full alpha.
// Called when your avatar changes to screenshot for save data.
uint32_t colorBuffer = currentMIPS - > r [ MIPS_REG_A1 ] ;
uint32_t depthBuffer = currentMIPS - > r [ MIPS_REG_T2 ] ;
uint32_t byteStride = currentMIPS - > r [ MIPS_REG_A2 ] ;
uint32_t height = currentMIPS - > r [ MIPS_REG_T1 ] ;
uint32_t size = byteStride * height ;
if ( ! Memory : : IsVRAMAddress ( colorBuffer ) | | ! Memory : : IsValidRange ( colorBuffer , size ) )
return 0 ;
if ( depthBuffer ! = 0 ) {
if ( ! Memory : : IsVRAMAddress ( colorBuffer ) | | ! Memory : : IsValidRange ( depthBuffer , size ) )
return 0 ;
// This is added to read from the linearized mirror.
uint32_t depthMirror = depthBuffer + 0x00200000 ;
// Depth download required, or it won't work and will be transparent.
gpu - > PerformMemoryCopy ( depthMirror , depthMirror , size , GPUCopyFlag : : FORCE_DST_MEM | GPUCopyFlag : : DEPTH_REQUESTED ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , depthMirror , size , " godseaterburst_depthmask_5551 " ) ;
}
gpu - > PerformMemoryDownload ( colorBuffer , size ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , colorBuffer , size , " godseaterburst_depthmask_5551 " ) ;
return 0 ;
}
2014-05-30 23:28:21 -07:00
static int Hook_hexyzforce_monoclome_thread ( ) {
u32 fb_info ;
if ( ! GetMIPSStaticAddress ( fb_info , - 4 , 0 ) ) {
return 0 ;
}
const u32 fb_address = Memory : : Read_U32 ( fb_info ) ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
2014-05-30 23:33:50 -07:00
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " hexyzforce_monoclome_thread " ) ;
2014-05-30 23:28:21 -07:00
}
return 0 ;
}
2014-05-31 18:27:02 -07:00
static int Hook_starocean_write_stencil ( ) {
2014-09-08 19:10:46 -07:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_T7 ] ;
2018-12-14 13:54:03 +01:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
2022-08-06 21:12:42 -07:00
gpu - > PerformStencilUpload ( fb_address , 0x00088000 , StencilUpload : : IGNORE_ALPHA ) ;
2014-05-31 18:27:02 -07:00
}
return 0 ;
}
2014-06-08 16:38:43 -07:00
static int Hook_topx_create_saveicon ( ) {
2014-09-08 19:10:46 -07:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_V0 ] ;
2014-06-08 16:38:43 -07:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " topx_create_saveicon " ) ;
2014-06-08 16:38:43 -07:00
}
return 0 ;
}
2014-06-26 01:38:22 -07:00
static int Hook_ff1_battle_effect ( ) {
2014-09-08 19:10:46 -07:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A1 ] ;
2014-06-26 01:38:22 -07:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " ff1_battle_effect " ) ;
2014-06-26 01:38:22 -07:00
}
return 0 ;
}
2014-07-13 08:36:34 -07:00
static int Hook_dissidia_recordframe_avi ( ) {
// This is called once per frame, and records that frame's data to avi.
2014-09-08 19:10:46 -07:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A1 ] ;
2014-07-13 08:36:34 -07:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " dissidia_recordframe_avi " ) ;
2014-07-13 08:36:34 -07:00
}
return 0 ;
}
2014-09-04 17:36:56 +09:00
static int Hook_brandish_download_frame ( ) {
u32 fb_infoaddr ;
if ( ! GetMIPSStaticAddress ( fb_infoaddr , 0x2c , 0x30 ) ) {
return 0 ;
}
const u32 fb_info = Memory : : Read_U32 ( fb_infoaddr ) ;
2014-09-04 23:44:32 +09:00
const MIPSOpcode fb_index_load = Memory : : Read_Instruction ( currentMIPS - > pc + 0x38 , true ) ;
if ( fb_index_load ! = MIPS_MAKE_LW ( MIPS_GET_RT ( fb_index_load ) , MIPS_GET_RS ( fb_index_load ) , fb_index_load & 0xffff ) ) {
return 0 ;
}
2014-09-05 00:31:25 +09:00
const int fb_index_offset = ( s16 ) ( fb_index_load & 0xffff ) ;
2014-09-04 23:44:32 +09:00
const u32 fb_index = ( Memory : : Read_U32 ( fb_info + fb_index_offset ) + 1 ) & 1 ;
2014-09-04 17:36:56 +09:00
const u32 fb_address = 0x4000000 + ( 0x44000 * fb_index ) ;
const u32 dest_address = currentMIPS - > r [ MIPS_REG_A1 ] ;
if ( Memory : : IsRAMAddress ( dest_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " brandish_download_frame " ) ;
2014-09-04 17:36:56 +09:00
}
return 0 ;
}
2014-09-08 19:10:46 -07:00
static int Hook_growlanser_create_saveicon ( ) {
2015-07-26 22:38:40 +02:00
const u32 fb_address = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_SP ] + 4 ) ;
const u32 fmt = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_SP ] ) ;
const u32 sz = fmt = = GE_FORMAT_8888 ? 0x00088000 : 0x00044000 ;
if ( Memory : : IsVRAMAddress ( fb_address ) & & fmt < = 3 ) {
gpu - > PerformMemoryDownload ( fb_address , sz ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , sz , " growlanser_create_saveicon " ) ;
2015-07-26 22:38:40 +02:00
}
return 0 ;
2014-09-08 19:10:46 -07:00
}
2014-09-08 22:44:27 -07:00
static int Hook_sd_gundam_g_generation_download_frame ( ) {
const u32 fb_address = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_SP ] + 8 ) ;
const u32 fmt = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_SP ] + 4 ) ;
const u32 sz = fmt = = GE_FORMAT_8888 ? 0x00088000 : 0x00044000 ;
if ( Memory : : IsVRAMAddress ( fb_address ) & & fmt < = 3 ) {
gpu - > PerformMemoryDownload ( fb_address , sz ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , sz , " sd_gundam_g_generation_download_frame " ) ;
2014-09-08 22:44:27 -07:00
}
return 0 ;
}
2014-09-09 14:33:15 +08:00
static int Hook_narisokonai_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_V0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " narisokonai_download_frame " ) ;
2014-09-09 14:33:15 +08:00
}
return 0 ;
}
2014-09-11 00:47:06 +08:00
static int Hook_kirameki_school_life_download_frame ( ) {
2014-09-11 22:56:53 -07:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A2 ] ;
2014-09-11 00:18:59 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
2014-09-11 22:56:53 -07:00
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " kirameki_school_life_download_frame " ) ;
2014-09-11 00:18:59 +08:00
}
return 0 ;
}
2014-09-11 14:34:17 +08:00
static int Hook_orenoimouto_download_frame ( ) {
2014-09-11 22:56:53 -07:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A4 ] ;
2014-09-11 14:34:17 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
2014-09-11 22:56:53 -07:00
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " orenoimouto_download_frame " ) ;
2014-09-11 14:34:17 +08:00
}
return 0 ;
}
2014-09-11 15:32:07 +08:00
static int Hook_sakurasou_download_frame ( ) {
2014-09-13 10:21:00 +08:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_V0 ] ;
2014-09-11 15:32:07 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
2014-09-13 10:21:00 +08:00
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " sakurasou_download_frame " ) ;
2014-09-11 15:32:07 +08:00
}
return 0 ;
}
2014-09-15 00:19:22 +08:00
static int Hook_suikoden1_and_2_download_frame_1 ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_S4 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " suikoden1_and_2_download_frame_1 " ) ;
2014-09-15 00:19:22 +08:00
}
return 0 ;
}
static int Hook_suikoden1_and_2_download_frame_2 ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_S2 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " suikoden1_and_2_download_frame_2 " ) ;
2014-09-15 00:19:22 +08:00
}
return 0 ;
}
2014-09-18 15:29:59 +08:00
static int Hook_rezel_cross_download_frame ( ) {
const u32 fb_address = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_SP ] + 0x1C ) ;
const u32 fmt = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_SP ] + 0x14 ) ;
const u32 sz = fmt = = GE_FORMAT_8888 ? 0x00088000 : 0x00044000 ;
if ( Memory : : IsVRAMAddress ( fb_address ) & & fmt < = 3 ) {
gpu - > PerformMemoryDownload ( fb_address , sz ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , sz , " rezel_cross_download_frame " ) ;
2014-09-18 15:29:59 +08:00
}
return 0 ;
}
2014-09-18 15:37:12 +08:00
static int Hook_kagaku_no_ensemble_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_V0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " kagaku_no_ensemble_download_frame " ) ;
2014-09-18 15:37:12 +08:00
}
return 0 ;
}
2014-09-26 16:55:37 +08:00
static int Hook_soranokiseki_fc_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A2 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " soranokiseki_fc_download_frame " ) ;
2014-09-26 16:55:37 +08:00
}
return 0 ;
}
2014-09-26 17:13:01 +08:00
static int Hook_soranokiseki_sc_download_frame ( ) {
u32 fb_infoaddr ;
if ( ! GetMIPSStaticAddress ( fb_infoaddr , 0x28 , 0x2C ) ) {
return 0 ;
}
const u32 fb_info = Memory : : Read_U32 ( fb_infoaddr ) ;
const MIPSOpcode fb_index_load = Memory : : Read_Instruction ( currentMIPS - > pc + 0x34 , true ) ;
if ( fb_index_load ! = MIPS_MAKE_LW ( MIPS_GET_RT ( fb_index_load ) , MIPS_GET_RS ( fb_index_load ) , fb_index_load & 0xffff ) ) {
return 0 ;
}
const int fb_index_offset = ( s16 ) ( fb_index_load & 0xffff ) ;
const u32 fb_index = ( Memory : : Read_U32 ( fb_info + fb_index_offset ) + 1 ) & 1 ;
const u32 fb_address = 0x4000000 + ( 0x44000 * fb_index ) ;
const u32 dest_address = currentMIPS - > r [ MIPS_REG_A1 ] ;
if ( Memory : : IsRAMAddress ( dest_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " soranokiseki_sc_download_frame " ) ;
2014-09-26 17:13:01 +08:00
}
return 0 ;
}
2014-09-27 14:00:37 +08:00
static int Hook_bokunonatsuyasumi4_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A3 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " bokunonatsuyasumi4_download_frame " ) ;
2014-09-27 14:00:37 +08:00
}
return 0 ;
}
2014-10-05 13:39:15 +08:00
static int Hook_danganronpa2_1_download_frame ( ) {
const u32 fb_base = currentMIPS - > r [ MIPS_REG_V0 ] ;
const u32 fb_offset = currentMIPS - > r [ MIPS_REG_V1 ] ;
const u32 fb_offset_fix = fb_offset & 0xFFFFFFFC ;
const u32 fb_address = fb_base + fb_offset_fix ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " danganronpa2_1_download_frame " ) ;
2014-10-05 13:39:15 +08:00
}
return 0 ;
}
2014-10-05 13:42:03 +08:00
static int Hook_danganronpa2_2_download_frame ( ) {
const u32 fb_base = currentMIPS - > r [ MIPS_REG_V0 ] ;
const u32 fb_offset = currentMIPS - > r [ MIPS_REG_V1 ] ;
const u32 fb_offset_fix = fb_offset & 0xFFFFFFFC ;
const u32 fb_address = fb_base + fb_offset_fix ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " danganronpa2_2_download_frame " ) ;
2014-10-05 13:42:03 +08:00
}
return 0 ;
}
2014-10-05 13:44:39 +08:00
static int Hook_danganronpa1_1_download_frame ( ) {
const u32 fb_base = currentMIPS - > r [ MIPS_REG_A5 ] ;
const u32 fb_offset = currentMIPS - > r [ MIPS_REG_V0 ] ;
const u32 fb_offset_fix = fb_offset & 0xFFFFFFFC ;
const u32 fb_address = fb_base + fb_offset_fix ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " danganronpa1_1_download_frame " ) ;
2014-10-05 13:44:39 +08:00
}
return 0 ;
}
2014-10-05 13:46:47 +08:00
static int Hook_danganronpa1_2_download_frame ( ) {
const MIPSOpcode instruction = Memory : : Read_Instruction ( currentMIPS - > pc + 0x8 , true ) ;
const int reg_num = instruction > > 11 & 31 ;
const u32 fb_base = currentMIPS - > r [ reg_num ] ;
const u32 fb_offset = currentMIPS - > r [ MIPS_REG_V0 ] ;
const u32 fb_offset_fix = fb_offset & 0xFFFFFFFC ;
const u32 fb_address = fb_base + fb_offset_fix ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " danganronpa1_2_download_frame " ) ;
2014-10-05 13:46:47 +08:00
}
return 0 ;
}
2014-12-03 18:44:04 +08:00
static int Hook_kankabanchoutbr_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A1 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " kankabanchoutbr_download_frame " ) ;
2014-12-03 18:44:04 +08:00
}
return 0 ;
}
2014-12-11 11:13:59 +08:00
static int Hook_orenoimouto_download_frame_2 ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A4 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " orenoimouto_download_frame_2 " ) ;
2014-12-11 11:13:59 +08:00
}
return 0 ;
}
2015-01-06 14:19:36 +08:00
static int Hook_rewrite_download_frame ( ) {
2015-01-06 17:37:33 +08:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
2015-01-06 14:19:36 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " rewrite_download_frame " ) ;
2015-01-06 14:19:36 +08:00
}
return 0 ;
}
2015-01-06 14:30:45 +08:00
static int Hook_kudwafter_download_frame ( ) {
2015-01-06 17:37:33 +08:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
2015-01-06 14:30:45 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " kudwafter_download_frame " ) ;
2015-01-06 14:30:45 +08:00
}
return 0 ;
}
2015-01-06 14:39:06 +08:00
static int Hook_kumonohatateni_download_frame ( ) {
2015-01-06 17:37:33 +08:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
2015-01-06 14:39:06 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " kumonohatateni_download_frame " ) ;
2015-01-06 14:39:06 +08:00
}
return 0 ;
}
2015-01-06 14:48:56 +08:00
static int Hook_otomenoheihou_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " otomenoheihou_download_frame " ) ;
2015-01-06 14:48:56 +08:00
}
return 0 ;
}
2015-01-06 15:05:30 +08:00
static int Hook_grisaianokajitsu_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " grisaianokajitsu_download_frame " ) ;
2015-01-06 15:05:30 +08:00
}
return 0 ;
}
2015-01-06 15:13:53 +08:00
static int Hook_kokoroconnect_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A3 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " kokoroconnect_download_frame " ) ;
2015-01-06 15:13:53 +08:00
}
return 0 ;
}
2015-01-06 15:22:44 +08:00
static int Hook_toheart2_download_frame ( ) {
2015-01-06 17:37:33 +08:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A1 ] ;
2015-01-06 15:22:44 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " toheart2_download_frame " ) ;
2015-01-06 15:22:44 +08:00
}
return 0 ;
}
2015-01-09 14:34:03 +08:00
static int Hook_toheart2_download_frame_2 ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " toheart2_download_frame_2 " ) ;
2015-01-09 14:34:03 +08:00
}
return 0 ;
}
2015-01-09 14:36:48 +08:00
static int Hook_flowers_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " flowers_download_frame " ) ;
2015-01-09 14:36:48 +08:00
}
return 0 ;
}
2015-01-13 15:42:25 +08:00
static int Hook_motorstorm_download_frame ( ) {
const u32 fb_address = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_A1 ] + 0x18 ) ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " motorstorm_download_frame " ) ;
2015-01-13 15:42:25 +08:00
}
return 0 ;
}
2015-01-14 17:27:14 +08:00
static int Hook_utawarerumono_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " utawarerumono_download_frame " ) ;
2015-07-26 22:38:40 +02:00
}
2015-01-14 17:27:14 +08:00
return 0 ;
}
2015-02-09 14:08:36 +08:00
static int Hook_photokano_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A1 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " photokano_download_frame " ) ;
2015-02-09 14:08:36 +08:00
}
return 0 ;
}
2015-02-12 20:36:17 +08:00
static int Hook_photokano_download_frame_2 ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A1 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " photokano_download_frame_2 " ) ;
2015-02-12 20:36:17 +08:00
}
return 0 ;
}
2015-03-18 11:43:31 +08:00
static int Hook_gakuenheaven_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " gakuenheaven_download_frame " ) ;
2015-07-26 22:38:40 +02:00
}
2015-03-18 11:43:31 +08:00
return 0 ;
}
2015-04-20 16:36:07 +08:00
static int Hook_youkosohitsujimura_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_V0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " youkosohitsujimura_download_frame " ) ;
2015-07-26 22:38:40 +02:00
}
2015-04-20 16:36:07 +08:00
return 0 ;
}
2021-02-07 09:02:28 -08:00
static int Hook_zettai_hero_update_minimap_tex ( ) {
const MIPSOpcode storeOffset = Memory : : Read_Instruction ( currentMIPS - > pc + 4 , true ) ;
const uint32_t texAddr = currentMIPS - > r [ MIPS_REG_A0 ] + SignExtend16ToS32 ( storeOffset ) ;
const uint32_t texSize = 64 * 64 * 1 ;
const uint32_t writeAddr = currentMIPS - > r [ MIPS_REG_V1 ] + SignExtend16ToS32 ( storeOffset ) ;
if ( Memory : : IsValidRange ( texAddr , texSize ) & & writeAddr > = texAddr & & writeAddr < texAddr + texSize ) {
const uint8_t currentValue = Memory : : Read_U8 ( writeAddr ) ;
if ( currentValue ! = currentMIPS - > r [ MIPS_REG_A3 ] ) {
gpu - > InvalidateCache ( texAddr , texSize , GPU_INVALIDATE_FORCE ) ;
}
}
return 0 ;
}
2016-01-02 13:33:22 +08:00
static int Hook_tonyhawkp8_upload_tutorial_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryUpload ( fb_address , 0x00088000 ) ;
}
return 0 ;
}
2016-01-03 09:56:40 +08:00
static int Hook_sdgundamggenerationportable_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A3 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " sdgundamggenerationportable_download_frame " ) ;
2016-01-03 09:56:40 +08:00
}
return 0 ;
}
2016-01-03 22:13:42 +08:00
static int Hook_atvoffroadfurypro_download_frame ( ) {
2016-01-04 09:08:52 +08:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_S2 ] ;
const u32 fb_size = ( currentMIPS - > r [ MIPS_REG_S4 ] > > 3 ) * currentMIPS - > r [ MIPS_REG_S3 ] ;
2016-01-03 22:13:42 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
2016-01-04 09:08:52 +08:00
gpu - > PerformMemoryDownload ( fb_address , fb_size ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , fb_size , " atvoffroadfurypro_download_frame " ) ;
2016-05-20 22:28:26 -07:00
}
2016-01-03 22:13:42 +08:00
return 0 ;
}
2016-01-03 09:56:40 +08:00
2016-01-03 22:20:50 +08:00
static int Hook_atvoffroadfuryblazintrails_download_frame ( ) {
2016-01-04 09:08:52 +08:00
const u32 fb_address = currentMIPS - > r [ MIPS_REG_S5 ] ;
const u32 fb_size = ( currentMIPS - > r [ MIPS_REG_S3 ] > > 3 ) * currentMIPS - > r [ MIPS_REG_S2 ] ;
2016-01-03 22:20:50 +08:00
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
2016-01-04 09:08:52 +08:00
gpu - > PerformMemoryDownload ( fb_address , fb_size ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , fb_size , " atvoffroadfuryblazintrails_download_frame " ) ;
2016-05-20 22:28:26 -07:00
}
2016-01-03 22:20:50 +08:00
return 0 ;
}
2016-01-03 22:28:38 +08:00
static int Hook_littlebustersce_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_A0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " littlebustersce_download_frame " ) ;
2016-05-20 22:28:26 -07:00
}
2016-01-03 22:28:38 +08:00
return 0 ;
}
2016-01-03 22:35:46 +08:00
static int Hook_shinigamitoshoujo_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_S2 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " shinigamitoshoujo_download_frame " ) ;
2016-01-03 22:35:46 +08:00
}
return 0 ;
}
2016-01-24 11:53:46 +08:00
static int Hook_atvoffroadfuryprodemo_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_S5 ] ;
const u32 fb_size = ( ( currentMIPS - > r [ MIPS_REG_A0 ] + currentMIPS - > r [ MIPS_REG_A1 ] ) > > 3 ) * currentMIPS - > r [ MIPS_REG_S2 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , fb_size ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , fb_size , " atvoffroadfuryprodemo_download_frame " ) ;
2016-05-20 22:28:26 -07:00
}
2016-01-24 11:53:46 +08:00
return 0 ;
}
2016-01-24 11:55:56 +08:00
static int Hook_unendingbloodycall_download_frame ( ) {
const u32 fb_address = currentMIPS - > r [ MIPS_REG_T3 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00088000 , " unendingbloodycall_download_frame " ) ;
2016-05-20 22:28:26 -07:00
}
2016-01-24 11:55:56 +08:00
return 0 ;
}
2016-01-24 11:58:20 +08:00
static int Hook_omertachinmokunookitethelegacy_download_frame ( ) {
const u32 fb_address = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_SP ] + 4 ) ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryDownload ( fb_address , 0x00044000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , 0x00044000 , " omertachinmokunookitethelegacy_download_frame " ) ;
2016-05-20 22:28:26 -07:00
}
return 0 ;
}
static int Hook_katamari_render_check ( ) {
const u32 fb_address = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_A0 ] + 0x3C ) ;
const u32 fbInfoPtr = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_A0 ] + 0x40 ) ;
if ( Memory : : IsVRAMAddress ( fb_address ) & & fbInfoPtr ! = 0 ) {
const u32 sizeInfoPtr = Memory : : Read_U32 ( fbInfoPtr + 0x0C ) ;
// These are the values it uses to control the loop.
// Width in memory appears to be stride / 8.
const u32 width = Memory : : Read_U16 ( sizeInfoPtr + 0x08 ) * 8 ;
// Height in memory is also divided by 8 (but this one isn't hardcoded.)
const u32 heightBlocks = Memory : : Read_U16 ( sizeInfoPtr + 0x0A ) ;
// For some reason this is the number of heightBlocks less 1.
const u32 heightBlockCount = Memory : : Read_U8 ( fbInfoPtr + 0x08 ) + 1 ;
const u32 totalBytes = width * heightBlocks * heightBlockCount ;
gpu - > PerformMemoryDownload ( fb_address , totalBytes ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , totalBytes , " katamari_render_check " ) ;
2016-05-20 22:28:26 -07:00
}
return 0 ;
2016-01-24 11:58:20 +08:00
}
2016-05-20 22:28:26 -07:00
static int Hook_katamari_screenshot_to_565 ( ) {
u32 fb_address ;
if ( GetMIPSStaticAddress ( fb_address , 0x0040 , 0x0044 ) ) {
gpu - > PerformMemoryDownload ( 0x04000000 | fb_address , 0x00088000 ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , 0x04000000 | fb_address , 0x00088000 , " katamari_screenshot_to_565 " ) ;
2016-05-20 22:28:26 -07:00
}
2016-01-24 11:58:20 +08:00
return 0 ;
}
2016-08-06 16:30:47 -07:00
static int Hook_mytranwars_upload_frame ( ) {
u32 fb_address = currentMIPS - > r [ MIPS_REG_S0 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) ) {
gpu - > PerformMemoryUpload ( fb_address , 0x00088000 ) ;
}
return 0 ;
}
2017-08-19 11:37:47 -07:00
static u32 marvelalliance1_copy_src = 0 ;
static u32 marvelalliance1_copy_dst = 0 ;
static u32 marvelalliance1_copy_size = 0 ;
static int Hook_marvelalliance1_copy_a1_before ( ) {
marvelalliance1_copy_src = currentMIPS - > r [ MIPS_REG_A1 ] ;
marvelalliance1_copy_dst = currentMIPS - > r [ MIPS_REG_V1 ] ;
marvelalliance1_copy_size = currentMIPS - > r [ MIPS_REG_V0 ] - currentMIPS - > r [ MIPS_REG_V1 ] ;
gpu - > PerformMemoryDownload ( marvelalliance1_copy_src , marvelalliance1_copy_size ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , marvelalliance1_copy_src , marvelalliance1_copy_size , " marvelalliance1_copy_a1_before " ) ;
2017-08-19 11:37:47 -07:00
return 0 ;
}
static int Hook_marvelalliance1_copy_a2_before ( ) {
marvelalliance1_copy_src = currentMIPS - > r [ MIPS_REG_A2 ] ;
marvelalliance1_copy_dst = currentMIPS - > r [ MIPS_REG_V0 ] ;
marvelalliance1_copy_size = currentMIPS - > r [ MIPS_REG_A1 ] - currentMIPS - > r [ MIPS_REG_A2 ] ;
gpu - > PerformMemoryDownload ( marvelalliance1_copy_src , marvelalliance1_copy_size ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , marvelalliance1_copy_src , marvelalliance1_copy_size , " marvelalliance1_copy_a2_before " ) ;
2017-08-19 11:37:47 -07:00
return 0 ;
}
static int Hook_marvelalliance1_copy_after ( ) {
gpu - > PerformMemoryUpload ( marvelalliance1_copy_dst , marvelalliance1_copy_size ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : READ , marvelalliance1_copy_dst , marvelalliance1_copy_size , " marvelalliance1_copy_after " ) ;
2017-08-19 11:37:47 -07:00
return 0 ;
}
2017-12-28 19:28:39 -08:00
static int Hook_starocean_clear_framebuf_before ( ) {
skipGPUReplacements | = ( int ) GPUReplacementSkip : : MEMSET ;
return 0 ;
}
static int Hook_starocean_clear_framebuf_after ( ) {
skipGPUReplacements & = ~ ( int ) GPUReplacementSkip : : MEMSET ;
// This hook runs after the copy, this is the final memcpy destination.
u32 framebuf = currentMIPS - > r [ MIPS_REG_V0 ] - 512 * 4 * 271 ;
u32 y_address , h_address ;
if ( GetMIPSGPAddress ( y_address , - 204 ) & & GetMIPSGPAddress ( h_address , - 200 ) ) {
int y = ( s16 ) Memory : : Read_U16 ( y_address ) ;
int h = ( s16 ) Memory : : Read_U16 ( h_address ) ;
DEBUG_LOG ( HLE , " starocean_clear_framebuf() - %08x y=%d-%d " , framebuf , y , h ) ;
// TODO: This is always clearing to 0, actually, which could be faster than an upload.
gpu - > PerformMemoryUpload ( framebuf + 512 * y * 4 , 512 * h * 4 ) ;
}
return 0 ;
}
2019-10-05 08:37:18 -07:00
static int Hook_motorstorm_pixel_read ( ) {
u32 fb_address = Memory : : Read_U32 ( currentMIPS - > r [ MIPS_REG_A0 ] + 0x18 ) ;
u32 fb_height = Memory : : Read_U16 ( currentMIPS - > r [ MIPS_REG_A0 ] + 0x26 ) ;
u32 fb_stride = Memory : : Read_U16 ( currentMIPS - > r [ MIPS_REG_A0 ] + 0x28 ) ;
gpu - > PerformMemoryDownload ( fb_address , fb_height * fb_stride ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , fb_height * fb_stride , " motorstorm_pixel_read " ) ;
2019-10-05 08:37:18 -07:00
return 0 ;
}
2021-01-17 12:38:03 -08:00
static int Hook_worms_copy_normalize_alpha ( ) {
// At this point in the function (0x0CC), s1 is the framebuf and a2 is the size.
u32 fb_address = currentMIPS - > r [ MIPS_REG_S1 ] ;
u32 fb_size = currentMIPS - > r [ MIPS_REG_A2 ] ;
if ( Memory : : IsVRAMAddress ( fb_address ) & & Memory : : IsValidRange ( fb_address , fb_size ) ) {
gpu - > PerformMemoryDownload ( fb_address , fb_size ) ;
2021-02-02 00:08:05 -08:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fb_address , fb_size , " worms_copy_normalize_alpha " ) ;
2021-01-17 12:38:03 -08:00
}
return 0 ;
}
2021-02-21 13:46:49 +08:00
static int Hook_openseason_data_decode ( ) {
static u32 firstWritePtr = 0 ;
u32 curWritePtr = currentMIPS - > r [ MIPS_REG_A0 ] ;
u32 endPtr = currentMIPS - > r [ MIPS_REG_A1 ] ;
u32 writeBytes = currentMIPS - > r [ MIPS_REG_V0 ] ;
u32 startPtr = curWritePtr - writeBytes ;
if ( Memory : : IsVRAMAddress ( startPtr ) & & ( firstWritePtr = = 0 | | startPtr < firstWritePtr ) ) {
firstWritePtr = startPtr ;
}
if ( Memory : : IsVRAMAddress ( endPtr ) & & curWritePtr = = endPtr ) {
gpu - > PerformMemoryUpload ( firstWritePtr , endPtr - firstWritePtr ) ;
firstWritePtr = 0 ;
}
return 0 ;
}
2022-05-22 11:44:49 -07:00
static int Hook_soltrigger_render_ucschar ( ) {
u32 targetInfoPtrPtr = currentMIPS - > r [ MIPS_REG_A2 ] ;
u32 targetInfoPtr = Memory : : IsValidRange ( targetInfoPtrPtr , 4 ) ? Memory : : ReadUnchecked_U32 ( targetInfoPtrPtr ) : 0 ;
if ( Memory : : IsValidRange ( targetInfoPtr , 32 ) ) {
u32 targetPtr = Memory : : Read_U32 ( targetInfoPtr + 8 ) ;
u32 targetByteStride = Memory : : Read_U32 ( targetInfoPtr + 16 ) ;
// We don't know the height specifically.
gpu - > InvalidateCache ( targetPtr , targetByteStride * 512 , GPU_INVALIDATE_HINT ) ;
}
return 0 ;
}
2022-07-02 19:14:05 +02:00
static int Hook_gow_fps_hack ( ) {
2022-07-04 00:58:48 +02:00
if ( PSP_CoreParameter ( ) . compat . flags ( ) . GoWFramerateHack60 | | PSP_CoreParameter ( ) . compat . flags ( ) . GoWFramerateHack30 ) {
if ( PSP_CoreParameter ( ) . compat . flags ( ) . GoWFramerateHack30 ) {
2022-07-02 19:47:51 +02:00
__DisplayWaitForVblanks ( " vblank start waited " , 2 ) ;
} else {
__DisplayWaitForVblanks ( " vblank start waited " , 1 ) ;
}
2022-07-02 19:14:05 +02:00
}
return 0 ;
}
2022-07-03 04:23:26 +02:00
static int Hook_gow_vortex_hack ( ) {
2022-07-04 00:58:48 +02:00
if ( PSP_CoreParameter ( ) . compat . flags ( ) . GoWFramerateHack60 ) {
2022-07-05 04:51:58 +02:00
// from my tests both ==0x3F800000 and !=0x3F800000 takes around 1:40-1:50, that seems to match correct behaviour
2022-07-03 22:18:44 +02:00
if ( currentMIPS - > r [ MIPS_REG_S1 ] = = 0 & & currentMIPS - > r [ MIPS_REG_A0 ] = = 0xC0 & & currentMIPS - > r [ MIPS_REG_T4 ] ! = 0x3F800000 ) {
2022-07-03 04:23:26 +02:00
currentMIPS - > r [ MIPS_REG_S1 ] = 1 ;
}
}
return 0 ;
}
2022-07-14 23:17:32 +02:00
static int Hook_ZZT3_select_hack ( ) {
if ( PSP_CoreParameter ( ) . compat . flags ( ) . ZZT3SelectHack ) {
if ( currentMIPS - > r [ MIPS_REG_V0 ] = = 0 ) {
currentMIPS - > r [ MIPS_REG_V0 ] = 1 ;
}
}
return 0 ;
}
2016-05-09 19:25:51 +02:00
# define JITFUNC(f) (&MIPSComp::MIPSFrontendInterface::f)
2014-12-07 14:25:22 +01:00
2013-11-30 20:57:44 +01:00
// Can either replace with C functions or functions emitted in Asm/ArmAsm.
static const ReplacementTableEntry entries [ ] = {
2013-12-17 23:40:27 +01:00
// TODO: I think some games can be helped quite a bit by implementing the
// double-precision soft-float routines: __adddf3, __subdf3 and so on. These
// should of course be implemented JIT style, inline.
2013-12-18 11:22:53 +01:00
2014-01-07 12:03:59 +01:00
/* These two collide (same hash) and thus can't be replaced :/
2014-08-03 13:12:45 -07:00
{ " asinf " , & Replace_asinf , 0 , REPFLAG_DISABLED } ,
{ " acosf " , & Replace_acosf , 0 , REPFLAG_DISABLED } ,
2014-01-07 12:03:59 +01:00
*/
2014-08-03 13:12:45 -07:00
{ " sinf " , & Replace_sinf , 0 , REPFLAG_DISABLED } ,
{ " cosf " , & Replace_cosf , 0 , REPFLAG_DISABLED } ,
{ " tanf " , & Replace_tanf , 0 , REPFLAG_DISABLED } ,
{ " atanf " , & Replace_atanf , 0 , REPFLAG_DISABLED } ,
{ " sqrtf " , & Replace_sqrtf , 0 , REPFLAG_DISABLED } ,
{ " atan2f " , & Replace_atan2f , 0 , REPFLAG_DISABLED } ,
{ " floorf " , & Replace_floorf , 0 , REPFLAG_DISABLED } ,
{ " ceilf " , & Replace_ceilf , 0 , REPFLAG_DISABLED } ,
2015-02-19 11:14:23 +01:00
2014-08-03 13:12:45 -07:00
{ " memcpy " , & Replace_memcpy , 0 , 0 } ,
2015-02-19 11:14:23 +01:00
{ " memcpy_jak " , & Replace_memcpy_jak , 0 , 0 } ,
2014-08-03 13:12:45 -07:00
{ " memcpy16 " , & Replace_memcpy16 , 0 , 0 } ,
{ " memcpy_swizzled " , & Replace_memcpy_swizzled , 0 , 0 } ,
{ " memmove " , & Replace_memmove , 0 , 0 } ,
{ " memset " , & Replace_memset , 0 , 0 } ,
2015-02-20 00:17:47 +01:00
{ " memset_jak " , & Replace_memset_jak , 0 , 0 } ,
2014-08-03 13:12:45 -07:00
{ " strlen " , & Replace_strlen , 0 , REPFLAG_DISABLED } ,
{ " strcpy " , & Replace_strcpy , 0 , REPFLAG_DISABLED } ,
{ " strncpy " , & Replace_strncpy , 0 , REPFLAG_DISABLED } ,
{ " strcmp " , & Replace_strcmp , 0 , REPFLAG_DISABLED } ,
{ " strncmp " , & Replace_strncmp , 0 , REPFLAG_DISABLED } ,
2014-12-07 14:25:22 +01:00
{ " fabsf " , & Replace_fabsf , JITFUNC ( Replace_fabsf ) , REPFLAG_ALLOWINLINE | REPFLAG_DISABLED } ,
2014-08-03 13:12:45 -07:00
{ " dl_write_matrix " , & Replace_dl_write_matrix , 0 , REPFLAG_DISABLED } , // &MIPSComp::Jit::Replace_dl_write_matrix, REPFLAG_DISABLED },
{ " dl_write_matrix_2 " , & Replace_dl_write_matrix , 0 , REPFLAG_DISABLED } ,
{ " gta_dl_write_matrix " , & Replace_gta_dl_write_matrix , 0 , REPFLAG_DISABLED } ,
2013-12-20 15:37:37 +01:00
// 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...
2013-12-21 12:36:30 +01:00
// Haven't investigated write_matrix_4 and 5 but I think they are similar to 1 and 2.
2013-12-20 15:37:37 +01:00
2014-08-03 13:12:45 -07:00
// { "vmmul_q_transp", &Replace_vmmul_q_transp, 0, REPFLAG_DISABLED },
2014-05-30 22:49:16 -07:00
2014-09-08 19:10:46 -07:00
{ " godseaterburst_blit_texture " , & Hook_godseaterburst_blit_texture , 0 , REPFLAG_HOOKENTER } ,
2022-10-09 00:52:35 -07:00
{ " godseaterburst_depthmask_5551 " , & Hook_godseaterburst_depthmask_5551 , 0 , REPFLAG_HOOKENTER } ,
2014-09-08 19:10:46 -07:00
{ " hexyzforce_monoclome_thread " , & Hook_hexyzforce_monoclome_thread , 0 , REPFLAG_HOOKENTER , 0x58 } ,
{ " starocean_write_stencil " , & Hook_starocean_write_stencil , 0 , REPFLAG_HOOKENTER , 0x260 } ,
{ " topx_create_saveicon " , & Hook_topx_create_saveicon , 0 , REPFLAG_HOOKENTER , 0x34 } ,
{ " ff1_battle_effect " , & Hook_ff1_battle_effect , 0 , REPFLAG_HOOKENTER } ,
2014-08-03 13:12:45 -07:00
// This is actually used in other games, not just Dissidia.
2014-09-08 19:10:46 -07:00
{ " dissidia_recordframe_avi " , & Hook_dissidia_recordframe_avi , 0 , REPFLAG_HOOKENTER } ,
{ " brandish_download_frame " , & Hook_brandish_download_frame , 0 , REPFLAG_HOOKENTER } ,
{ " growlanser_create_saveicon " , & Hook_growlanser_create_saveicon , 0 , REPFLAG_HOOKENTER , 0x7C } ,
2014-09-08 22:44:27 -07:00
{ " sd_gundam_g_generation_download_frame " , & Hook_sd_gundam_g_generation_download_frame , 0 , REPFLAG_HOOKENTER , 0x48 } ,
2014-09-09 14:33:15 +08:00
{ " narisokonai_download_frame " , & Hook_narisokonai_download_frame , 0 , REPFLAG_HOOKENTER , 0x14 } ,
2014-09-11 22:56:53 -07:00
{ " kirameki_school_life_download_frame " , & Hook_kirameki_school_life_download_frame , 0 , REPFLAG_HOOKENTER } ,
{ " orenoimouto_download_frame " , & Hook_orenoimouto_download_frame , 0 , REPFLAG_HOOKENTER } ,
2014-09-13 10:21:00 +08:00
{ " sakurasou_download_frame " , & Hook_sakurasou_download_frame , 0 , REPFLAG_HOOKENTER , 0xF8 } ,
2014-09-15 00:19:22 +08:00
{ " suikoden1_and_2_download_frame_1 " , & Hook_suikoden1_and_2_download_frame_1 , 0 , REPFLAG_HOOKENTER , 0x9C } ,
{ " suikoden1_and_2_download_frame_2 " , & Hook_suikoden1_and_2_download_frame_2 , 0 , REPFLAG_HOOKENTER , 0x48 } ,
2014-09-18 15:29:59 +08:00
{ " rezel_cross_download_frame " , & Hook_rezel_cross_download_frame , 0 , REPFLAG_HOOKENTER , 0x54 } ,
2014-09-21 10:58:56 +08:00
{ " kagaku_no_ensemble_download_frame " , & Hook_kagaku_no_ensemble_download_frame , 0 , REPFLAG_HOOKENTER , 0x38 } ,
2014-09-26 16:55:37 +08:00
{ " soranokiseki_fc_download_frame " , & Hook_soranokiseki_fc_download_frame , 0 , REPFLAG_HOOKENTER , 0x180 } ,
2014-09-26 17:13:01 +08:00
{ " soranokiseki_sc_download_frame " , & Hook_soranokiseki_sc_download_frame , 0 , REPFLAG_HOOKENTER , } ,
2014-09-27 14:00:37 +08:00
{ " bokunonatsuyasumi4_download_frame " , & Hook_bokunonatsuyasumi4_download_frame , 0 , REPFLAG_HOOKENTER , 0x8C } ,
2014-10-05 13:39:15 +08:00
{ " danganronpa2_1_download_frame " , & Hook_danganronpa2_1_download_frame , 0 , REPFLAG_HOOKENTER , 0x68 } ,
2014-10-05 13:42:03 +08:00
{ " danganronpa2_2_download_frame " , & Hook_danganronpa2_2_download_frame , 0 , REPFLAG_HOOKENTER , 0x94 } ,
2014-10-05 13:44:39 +08:00
{ " danganronpa1_1_download_frame " , & Hook_danganronpa1_1_download_frame , 0 , REPFLAG_HOOKENTER , 0x78 } ,
2014-10-05 13:46:47 +08:00
{ " danganronpa1_2_download_frame " , & Hook_danganronpa1_2_download_frame , 0 , REPFLAG_HOOKENTER , 0xA8 } ,
2014-12-03 18:44:04 +08:00
{ " kankabanchoutbr_download_frame " , & Hook_kankabanchoutbr_download_frame , 0 , REPFLAG_HOOKENTER , } ,
2014-12-11 11:13:59 +08:00
{ " orenoimouto_download_frame_2 " , & Hook_orenoimouto_download_frame_2 , 0 , REPFLAG_HOOKENTER , } ,
2015-01-06 17:37:33 +08:00
{ " rewrite_download_frame " , & Hook_rewrite_download_frame , 0 , REPFLAG_HOOKENTER , 0x5C } ,
{ " kudwafter_download_frame " , & Hook_kudwafter_download_frame , 0 , REPFLAG_HOOKENTER , 0x58 } ,
{ " kumonohatateni_download_frame " , & Hook_kumonohatateni_download_frame , 0 , REPFLAG_HOOKENTER , } ,
2015-01-06 14:48:56 +08:00
{ " otomenoheihou_download_frame " , & Hook_otomenoheihou_download_frame , 0 , REPFLAG_HOOKENTER , 0x14 } ,
2015-01-06 15:05:30 +08:00
{ " grisaianokajitsu_download_frame " , & Hook_grisaianokajitsu_download_frame , 0 , REPFLAG_HOOKENTER , 0x14 } ,
2015-01-06 15:13:53 +08:00
{ " kokoroconnect_download_frame " , & Hook_kokoroconnect_download_frame , 0 , REPFLAG_HOOKENTER , 0x60 } ,
2015-01-06 17:37:33 +08:00
{ " toheart2_download_frame " , & Hook_toheart2_download_frame , 0 , REPFLAG_HOOKENTER , } ,
2015-01-09 14:34:03 +08:00
{ " toheart2_download_frame_2 " , & Hook_toheart2_download_frame_2 , 0 , REPFLAG_HOOKENTER , 0x18 } ,
2015-01-09 14:36:48 +08:00
{ " flowers_download_frame " , & Hook_flowers_download_frame , 0 , REPFLAG_HOOKENTER , 0x44 } ,
2015-01-13 15:42:25 +08:00
{ " motorstorm_download_frame " , & Hook_motorstorm_download_frame , 0 , REPFLAG_HOOKENTER , } ,
2015-01-14 17:27:14 +08:00
{ " utawarerumono_download_frame " , & Hook_utawarerumono_download_frame , 0 , REPFLAG_HOOKENTER , } ,
2015-02-09 14:08:36 +08:00
{ " photokano_download_frame " , & Hook_photokano_download_frame , 0 , REPFLAG_HOOKENTER , 0x2C } ,
2015-02-12 20:36:17 +08:00
{ " photokano_download_frame_2 " , & Hook_photokano_download_frame_2 , 0 , REPFLAG_HOOKENTER , } ,
2015-03-18 11:43:31 +08:00
{ " gakuenheaven_download_frame " , & Hook_gakuenheaven_download_frame , 0 , REPFLAG_HOOKENTER , } ,
2015-04-20 16:36:07 +08:00
{ " youkosohitsujimura_download_frame " , & Hook_youkosohitsujimura_download_frame , 0 , REPFLAG_HOOKENTER , 0x94 } ,
2021-02-07 09:02:28 -08:00
{ " zettai_hero_update_minimap_tex " , & Hook_zettai_hero_update_minimap_tex , 0 , REPFLAG_HOOKEXIT , } ,
2016-01-02 13:33:22 +08:00
{ " tonyhawkp8_upload_tutorial_frame " , & Hook_tonyhawkp8_upload_tutorial_frame , 0 , REPFLAG_HOOKENTER , } ,
2016-01-03 09:56:40 +08:00
{ " sdgundamggenerationportable_download_frame " , & Hook_sdgundamggenerationportable_download_frame , 0 , REPFLAG_HOOKENTER , 0x34 } ,
2016-01-04 09:08:52 +08:00
{ " atvoffroadfurypro_download_frame " , & Hook_atvoffroadfurypro_download_frame , 0 , REPFLAG_HOOKENTER , 0xA0 } ,
{ " atvoffroadfuryblazintrails_download_frame " , & Hook_atvoffroadfuryblazintrails_download_frame , 0 , REPFLAG_HOOKENTER , 0x80 } ,
2016-01-03 22:28:38 +08:00
{ " littlebustersce_download_frame " , & Hook_littlebustersce_download_frame , 0 , REPFLAG_HOOKENTER , } ,
2016-01-03 22:35:46 +08:00
{ " shinigamitoshoujo_download_frame " , & Hook_shinigamitoshoujo_download_frame , 0 , REPFLAG_HOOKENTER , 0xBC } ,
2016-01-24 11:53:46 +08:00
{ " atvoffroadfuryprodemo_download_frame " , & Hook_atvoffroadfuryprodemo_download_frame , 0 , REPFLAG_HOOKENTER , 0x80 } ,
2016-01-24 11:55:56 +08:00
{ " unendingbloodycall_download_frame " , & Hook_unendingbloodycall_download_frame , 0 , REPFLAG_HOOKENTER , 0x54 } ,
2016-01-24 11:58:20 +08:00
{ " omertachinmokunookitethelegacy_download_frame " , & Hook_omertachinmokunookitethelegacy_download_frame , 0 , REPFLAG_HOOKENTER , 0x88 } ,
2016-05-20 22:28:26 -07:00
{ " katamari_render_check " , & Hook_katamari_render_check , 0 , REPFLAG_HOOKENTER , 0 , } ,
{ " katamari_screenshot_to_565 " , & Hook_katamari_screenshot_to_565 , 0 , REPFLAG_HOOKENTER , 0 } ,
2016-08-06 16:30:47 -07:00
{ " mytranwars_upload_frame " , & Hook_mytranwars_upload_frame , 0 , REPFLAG_HOOKENTER , 0x128 } ,
2017-08-19 11:37:47 -07:00
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_a1_before , 0 , REPFLAG_HOOKENTER , 0x284 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_after , 0 , REPFLAG_HOOKENTER , 0x2bc } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_a1_before , 0 , REPFLAG_HOOKENTER , 0x2e8 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_after , 0 , REPFLAG_HOOKENTER , 0x320 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_a2_before , 0 , REPFLAG_HOOKENTER , 0x3b0 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_after , 0 , REPFLAG_HOOKENTER , 0x3e8 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_a2_before , 0 , REPFLAG_HOOKENTER , 0x410 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_after , 0 , REPFLAG_HOOKENTER , 0x448 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_a1_before , 0 , REPFLAG_HOOKENTER , 0x600 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_after , 0 , REPFLAG_HOOKENTER , 0x638 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_a1_before , 0 , REPFLAG_HOOKENTER , 0x664 } ,
{ " marvelalliance1_copy " , & Hook_marvelalliance1_copy_after , 0 , REPFLAG_HOOKENTER , 0x69c } ,
2017-12-28 19:28:39 -08:00
{ " starocean_clear_framebuf " , & Hook_starocean_clear_framebuf_before , 0 , REPFLAG_HOOKENTER , 0 } ,
{ " starocean_clear_framebuf " , & Hook_starocean_clear_framebuf_after , 0 , REPFLAG_HOOKEXIT , 0 } ,
2019-10-05 08:37:18 -07:00
{ " motorstorm_pixel_read " , & Hook_motorstorm_pixel_read , 0 , REPFLAG_HOOKENTER , 0 } ,
2021-01-17 12:38:03 -08:00
{ " worms_copy_normalize_alpha " , & Hook_worms_copy_normalize_alpha , 0 , REPFLAG_HOOKENTER , 0x0CC } ,
2021-02-21 13:46:49 +08:00
{ " openseason_data_decode " , & Hook_openseason_data_decode , 0 , REPFLAG_HOOKENTER , 0x2F0 } ,
2022-05-22 11:44:49 -07:00
{ " soltrigger_render_ucschar " , & Hook_soltrigger_render_ucschar , 0 , REPFLAG_HOOKENTER , 0 } ,
2022-07-02 19:14:05 +02:00
{ " gow_fps_hack " , & Hook_gow_fps_hack , 0 , REPFLAG_HOOKEXIT , 0 } ,
2022-07-03 04:23:26 +02:00
{ " gow_vortex_hack " , & Hook_gow_vortex_hack , 0 , REPFLAG_HOOKENTER , 0x60 } ,
2022-07-14 23:17:32 +02:00
{ " ZZT3_select_hack " , & Hook_ZZT3_select_hack , 0 , REPFLAG_HOOKENTER , 0xC4 } ,
2013-12-17 23:40:27 +01:00
{ }
2013-11-30 20:57:44 +01:00
} ;
2014-04-18 19:00:08 +02:00
2013-12-17 23:40:27 +01:00
static std : : map < u32 , u32 > replacedInstructions ;
2016-09-24 14:23:17 -07:00
static std : : unordered_map < std : : string , std : : vector < int > > replacementNameLookup ;
2013-12-17 23:40:27 +01:00
void Replacement_Init ( ) {
2014-08-03 13:17:55 -07:00
for ( int i = 0 ; i < ( int ) ARRAY_SIZE ( entries ) ; i + + ) {
const auto entry = & entries [ i ] ;
if ( ! entry - > name | | ( entry - > flags & REPFLAG_DISABLED ) ! = 0 )
continue ;
2015-02-21 15:48:09 -08:00
replacementNameLookup [ entry - > name ] . push_back ( i ) ;
2014-08-03 13:17:55 -07:00
}
2017-12-28 19:28:39 -08:00
skipGPUReplacements = 0 ;
2013-12-17 23:40:27 +01:00
}
void Replacement_Shutdown ( ) {
replacedInstructions . clear ( ) ;
2014-08-03 13:17:55 -07:00
replacementNameLookup . clear ( ) ;
2013-12-17 23:40:27 +01:00
}
2013-11-30 20:57:44 +01:00
int GetNumReplacementFuncs ( ) {
return ARRAY_SIZE ( entries ) ;
}
2015-02-21 15:48:09 -08:00
std : : vector < int > GetReplacementFuncIndexes ( u64 hash , int funcSize ) {
2013-12-17 23:40:27 +01:00
const char * name = MIPSAnalyst : : LookupHash ( hash , funcSize ) ;
2015-02-21 15:48:09 -08:00
std : : vector < int > emptyResult ;
2013-12-17 23:40:27 +01:00
if ( ! name ) {
2015-02-21 15:48:09 -08:00
return emptyResult ;
2013-12-17 23:40:27 +01:00
}
2014-08-03 13:17:55 -07:00
auto index = replacementNameLookup . find ( name ) ;
if ( index ! = replacementNameLookup . end ( ) ) {
return index - > second ;
2013-11-30 20:57:44 +01:00
}
2015-02-21 15:48:09 -08:00
return emptyResult ;
2013-11-30 20:57:44 +01:00
}
const ReplacementTableEntry * GetReplacementFunc ( int i ) {
return & entries [ i ] ;
2013-12-17 23:40:27 +01:00
}
2016-01-21 22:17:25 -08:00
static bool WriteReplaceInstruction ( u32 address , int index ) {
u32 prevInstr = Memory : : Read_Instruction ( address , false ) . encoding ;
2014-05-30 22:45:06 -07:00
if ( MIPS_IS_REPLACEMENT ( prevInstr ) ) {
2016-01-21 22:17:25 -08:00
int prevIndex = prevInstr & MIPS_EMUHACK_VALUE_MASK ;
if ( prevIndex = = index ) {
return false ;
}
WARN_LOG ( HLE , " Replacement func changed at %08x (%d -> %d) " , address , prevIndex , index ) ;
// Make sure we don't save the old replacement.
prevInstr = replacedInstructions [ address ] ;
2014-05-30 22:45:06 -07:00
}
2016-01-21 22:17:25 -08:00
if ( MIPS_IS_RUNBLOCK ( Memory : : Read_U32 ( address ) ) ) {
WARN_LOG ( HLE , " Replacing jitted func address %08x " , address ) ;
2014-05-30 22:45:06 -07:00
}
replacedInstructions [ address ] = prevInstr ;
Memory : : Write_U32 ( MIPS_EMUHACK_CALL_REPLACEMENT | index , address ) ;
2016-01-21 22:17:25 -08:00
return true ;
2014-05-30 22:45:06 -07:00
}
void WriteReplaceInstructions ( u32 address , u64 hash , int size ) {
2015-02-21 15:48:09 -08:00
std : : vector < int > indexes = GetReplacementFuncIndexes ( hash , size ) ;
for ( int index : indexes ) {
2016-01-21 22:17:25 -08:00
bool didReplace = false ;
2014-05-30 22:45:06 -07:00
auto entry = GetReplacementFunc ( index ) ;
if ( entry - > flags & REPFLAG_HOOKEXIT ) {
// When hooking func exit, we search for jr ra, and replace those.
for ( u32 offset = 0 ; offset < ( u32 ) size ; offset + = 4 ) {
2016-01-21 22:17:25 -08:00
const u32 op = Memory : : Read_Instruction ( address + offset , false ) . encoding ;
2014-05-30 22:45:06 -07:00
if ( op = = MIPS_MAKE_JR_RA ( ) ) {
2016-01-21 22:17:25 -08:00
if ( WriteReplaceInstruction ( address + offset , index ) ) {
didReplace = true ;
}
2014-05-30 22:45:06 -07:00
}
}
2014-05-30 23:28:21 -07:00
} else if ( entry - > flags & REPFLAG_HOOKENTER ) {
2016-01-21 22:17:25 -08:00
if ( WriteReplaceInstruction ( address + entry - > hookOffset , index ) ) {
didReplace = true ;
}
2014-05-30 22:45:06 -07:00
} else {
2016-01-21 22:17:25 -08:00
if ( WriteReplaceInstruction ( address , index ) ) {
didReplace = true ;
}
}
if ( didReplace ) {
INFO_LOG ( HLE , " Replaced %s at %08x with hash %016llx " , entries [ index ] . name , address , hash ) ;
2013-12-20 13:51:26 +01:00
}
2013-12-17 23:40:27 +01:00
}
}
2013-12-18 11:42:19 +01:00
2014-04-12 01:13:50 -07:00
void RestoreReplacedInstruction ( u32 address ) {
const u32 curInstr = Memory : : Read_U32 ( address ) ;
if ( MIPS_IS_REPLACEMENT ( curInstr ) ) {
Memory : : Write_U32 ( replacedInstructions [ address ] , address ) ;
2016-01-21 22:17:25 -08:00
NOTICE_LOG ( HLE , " Restored replaced func at %08x " , address ) ;
} else {
NOTICE_LOG ( HLE , " Replaced func changed at %08x " , address ) ;
2014-04-12 01:13:50 -07:00
}
replacedInstructions . erase ( address ) ;
}
void RestoreReplacedInstructions ( u32 startAddr , u32 endAddr ) {
2015-07-07 01:12:42 +02:00
if ( endAddr = = startAddr )
return ;
2014-04-14 07:57:28 -07:00
// Need to be in order, or we'll hang.
2014-04-17 22:47:05 -07:00
if ( endAddr < startAddr )
2014-04-14 07:57:28 -07:00
std : : swap ( endAddr , startAddr ) ;
2014-04-12 01:13:50 -07:00
const auto start = replacedInstructions . lower_bound ( startAddr ) ;
const auto end = replacedInstructions . upper_bound ( endAddr ) ;
int restored = 0 ;
for ( auto it = start ; it ! = end ; + + it ) {
const u32 addr = it - > first ;
const u32 curInstr = Memory : : Read_U32 ( addr ) ;
if ( MIPS_IS_REPLACEMENT ( curInstr ) ) {
Memory : : Write_U32 ( it - > second , addr ) ;
+ + restored ;
}
}
INFO_LOG ( HLE , " Restored %d replaced funcs between %08x-%08x " , restored , startAddr , endAddr ) ;
replacedInstructions . erase ( start , end ) ;
}
2014-05-27 07:50:08 -07:00
std : : map < u32 , u32 > SaveAndClearReplacements ( ) {
std : : map < u32 , u32 > saved ;
for ( auto it = replacedInstructions . begin ( ) , end = replacedInstructions . end ( ) ; it ! = end ; + + it ) {
const u32 addr = it - > first ;
const u32 curInstr = Memory : : Read_U32 ( addr ) ;
if ( MIPS_IS_REPLACEMENT ( curInstr ) ) {
saved [ addr ] = curInstr ;
Memory : : Write_U32 ( it - > second , addr ) ;
}
}
return saved ;
}
void RestoreSavedReplacements ( const std : : map < u32 , u32 > & saved ) {
for ( auto it = saved . begin ( ) , end = saved . end ( ) ; it ! = end ; + + it ) {
const u32 addr = it - > first ;
// Just put the replacements back.
Memory : : Write_U32 ( it - > second , addr ) ;
}
}
2013-12-18 11:42:19 +01:00
bool GetReplacedOpAt ( u32 address , u32 * op ) {
2013-12-20 13:51:26 +01:00
u32 instr = Memory : : Read_Opcode_JIT ( address ) . encoding ;
2013-12-18 16:27:23 +01:00
if ( MIPS_IS_REPLACEMENT ( instr ) ) {
auto iter = replacedInstructions . find ( address ) ;
if ( iter ! = replacedInstructions . end ( ) ) {
* op = iter - > second ;
return true ;
} else {
return false ;
}
2013-12-18 11:42:19 +01:00
}
2013-12-20 13:51:26 +01:00
return false ;
2013-12-18 11:42:19 +01:00
}
2015-04-12 13:35:10 -07:00
bool CanReplaceJalTo ( u32 dest , const ReplacementTableEntry * * entry , u32 * funcSize ) {
MIPSOpcode op ( Memory : : Read_Opcode_JIT ( dest ) ) ;
if ( ! MIPS_IS_REPLACEMENT ( op . encoding ) )
return false ;
// Make sure we don't replace if there are any breakpoints inside.
2015-10-31 23:01:19 +01:00
* funcSize = g_symbolMap - > GetFunctionSize ( dest ) ;
2015-04-12 13:35:10 -07:00
if ( * funcSize = = SymbolMap : : INVALID_ADDRESS ) {
if ( CBreakPoints : : IsAddressBreakPoint ( dest ) ) {
return false ;
}
* funcSize = ( u32 ) sizeof ( u32 ) ;
} else {
if ( CBreakPoints : : RangeContainsBreakPoint ( dest , * funcSize ) ) {
return false ;
}
}
int index = op . encoding & MIPS_EMUHACK_VALUE_MASK ;
* entry = GetReplacementFunc ( index ) ;
if ( ! * entry ) {
ERROR_LOG ( HLE , " ReplaceJalTo: Invalid replacement op %08x at %08x " , op . encoding , dest ) ;
return false ;
}
if ( ( * entry ) - > flags & ( REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT | REPFLAG_DISABLED ) ) {
// If it's a hook, we can't replace the jal, we have to go inside the func.
return false ;
}
return true ;
}