akmenu-next/arm9/source/gbasleep.cpp
2024-10-04 22:35:39 -07:00

287 lines
7.7 KiB
C++

/*
gbasleep.cpp
Copyright (C) 2007 By Dan Weiss (Dwedit)
Copyright (C) 2008-2009 somebody
Copyright (C) 2009 yellow wood goblin
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, either version 3 of the License, or
(at your option) any later version.
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 for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gbapatcher.h"
#include <stdio.h>
#include <string.h>
#include "gba_sleep_patch_bin.h"
const int ACTIVATION_SIZE=36;
const int FAKE_SIZE=512*1024; //search in first 512k
const int PATCH_LENGTH=2048;
void CGbaPatcher::CommonSleepSearch(void)
{
//The LDR,=xxxxxxxx ARM instruction:
//
//Load/Store=1
//Writeback=0
//Byte/Word=0
//Up/Down=either
//Pre/Post=?
//Immediate Offset=0
//rn = r15
//cond= always
//1110 01 0 p u 0 0 1 1111 rrrr oooooooooooo
//Instruction Mask:
//1111 11 1 0 0 1 1 1 1111 0000 000000000000
//Instruction Bits:
//1110 01 0 0 0 0 0 1 1111 0000 000000000000
//The STR rd,[rn] ARM instruction:
//Load/Store=0
//Writeback=0
//Byte/Word=0
//Up/Down=?
//Pre/Post=?
//Immediate Offset=0
//cond= always
//1110 01 0 p u 0 0 0 rnrn rdrd 000000000000
//MASK:
//1111 11 1 0 0 1 1 1 0000 0000 111111111111
//BITS:
//1110 01 0 0 0 0 0 0 0000 0000 000000000000
//THUMB:
//LDR rd,=xxxxxxxx
//01001rrroooooooo
//1111100000000000
//0100100000000000
//STR rd,[rb]
//01100 00000 bbb ddd
//11111 11111 000 000
//01100 00000 000 000
const u32 ldr_mask=0xFE7F0000;
const u32 ldr_bits=0xE41F0000;
const u32 str_mask=0xFE700FFF;
const u32 str_bits=0xE4000000;
const u32 t_ldr_mask=0xF800;
const u32 t_ldr_bits=0x4800;
const u32 t_str_mask=0xFFC0;
const u32 t_str_bits=0x6000;
u32 reg_data[16]; //contents of register
u32 reg_addr[16]; //address of the data which got written
u32 reg_lastwrite[16]; //PC at which the data was written to
memset(reg_data,0,sizeof(reg_data));
memset(reg_addr,0,sizeof(reg_addr));
memset(reg_lastwrite,0,sizeof(reg_lastwrite));
SetTrimSize();
if((iTrimSize+PATCH_LENGTH)>iWriter->MaxSize()) return;
u32 fake_size=FAKE_SIZE;
if(fake_size>iTrimSize) fake_size=iTrimSize;
//arm
u32 patch_location=iTrimSize;
u32 activation_location=patch_location+gba_sleep_patch_bin_size;
u32 search_size=fake_size/sizeof(u32);
for(u32 ii=0;ii<search_size;ii++)
{
u32 word=Data()[ii];
//check for LDR xx,=xxxxxxxx
if((word&ldr_mask)==ldr_bits)
{
u32 pc=ii*sizeof(u32)+8;
//1110 01 0 p u 0 0 1 1111 rrrr oooooooooooo
//0000 00 0 0 1 0 0 0 0000 0000 000000000000
int updown=-1;
if((word&0x800000))
{
updown=1;
}
int rd=((word>>12)&0x0F);
int offset=(word&0xFFF);
u32 address=(updown)*offset + pc;
u32 memdata=0xDEADBEEF;
if(address>=0&&address<iSize-3)
{
if((address&0x03)==0x00)
{
memdata=Data()[address/sizeof(u32)];
}
}
reg_data[rd]=memdata;
reg_addr[rd]=address;
reg_lastwrite[rd]=ii*sizeof(u32);
}
else if((word&str_mask)==str_bits)
{
//1110 01 0 p u 0 0 0 rnrn rdrd 000000000000
int rn,rd;
rn=((word>>16)&0x0F);
rd=((word>>12)&0x0F);
u32 myaddress=ii*sizeof(u32);
bool okay=true
&&(reg_data[rn]==0x03007FFC)
&&(myaddress-reg_lastwrite[rd]<64)
&&(myaddress-reg_lastwrite[rn]<64)
&&(reg_lastwrite[rd]!=0)
&&(reg_lastwrite[rn]!=0)
&&((reg_data[rd]&0xFF000000)==0x03000000)
;
if(!okay&&reg_data[rn]==0x03007FFC)
{
if(myaddress==0xE0)
{
okay=true;
}
}
if(myaddress<reg_lastwrite[rd]||myaddress<reg_lastwrite[rn])
{
okay=false;
}
if(okay)
{
{
//str rd,[rn] >> BX rn
Add2(myaddress,0xE12FFF10+rn);
//ldr rn,=XXXX >> ldr rn,=activation_jump
u32 data_address=reg_addr[rn];
if(data_address>=0&&data_address<iSize-3)
{
Add2(data_address,0x08000000+activation_location);
}
}
Add3(activation_location,myaddress+4,rd,rn);
activation_location+=ACTIVATION_SIZE;
}
}
}
//thumb
memset(reg_data,0,sizeof(reg_data));
memset(reg_addr,0,sizeof(reg_addr));
memset(reg_lastwrite,0,sizeof(reg_lastwrite));
search_size=fake_size/sizeof(u16);
//this code is Little Endian Only
for(u32 ii=0;ii<search_size;ii++)
{
u16 word=Data16()[ii];
//check for LDR xx,=xxxxxxxx
if ((word&t_ldr_mask)==t_ldr_bits)
{
u32 pc=ii*sizeof(u16)+4;
//01001rrroooooooo
int rd=((word>>8)&0x07);
int offset=(word&0xFF);
u32 address=offset*4+pc;
u32 memdata=0xDEADBEEF;
address&=~0x03;
if(address>=0&&address<iSize-3)
{
if((address&0x03)==0x00)
{
memdata=Data()[address/sizeof(u32)];
}
}
reg_data[rd]=memdata;
reg_addr[rd]=address;
reg_lastwrite[rd]=ii*sizeof(u16);
}
else if((word&t_str_mask)==t_str_bits)
{
//01100 00000 bbb ddd
int rb,rd;
rb=((word>>3)&0x07);
rd=((word>>0)&0x07);
u32 myaddress=ii*sizeof(u16);
bool okay=true
&&(reg_data[rb]==0x03007FFC)
&&(myaddress-reg_lastwrite[rd]<80)
&&(myaddress-reg_lastwrite[rb]<64)
&&(reg_lastwrite[rd]!=0)
&&(reg_lastwrite[rb]!=0)
&&((reg_data[rd]&0xFF000000)==0x03000000)
;
if(okay)
{
{
//str rd,[rn] >> BX rb
Add4(myaddress,0x4700+(rb<<3));
//ldr rn,=XXXX >> ldr rn,=activation_jump
u32 data_address=reg_addr[rb];
if(data_address>=0&&data_address<iSize-3)
{
Add2(data_address,0x08000000+activation_location);
}
}
Add3(activation_location,myaddress+1+2,rd,rb);
activation_location+=ACTIVATION_SIZE;
reg_data[rb]=0; //for mario kart. may regress for another games.
}
}
}
}
void CGbaPatcher::SetTrimSize(void)
{
u8 byte=Data8()[iSize-1];
u32 bottom=iSize-2,top=bottom-PATCH_LENGTH-16,alignedSize=iSize+(16-(iSize&15));
for(u32 ii=bottom;ii>=top;ii--)
{
if(Data8()[ii]!=byte||ii==top)
{
iTrimSize=ii+4;
iTrimSize=iTrimSize+(16-(iTrimSize&15));
if(iTrimSize>alignedSize) iTrimSize=alignedSize;
break;
}
}
}
void CGbaPatcher::CommonSleepPatch(void)
{
if(!(iCount2+iCount4)) return;
for(u32 ii=0;ii<iCount2;ii++)
{
iWriter->Write(iPatchInfo2[ii].iOffset,(u8*)&(iPatchInfo2[ii].iValue),sizeof(iPatchInfo2[ii].iValue));
}
for(u32 ii=0;ii<iCount4;ii++)
{
iWriter->Write(iPatchInfo4[ii].iOffset,(u8*)&(iPatchInfo4[ii].iValue),sizeof(iPatchInfo4[ii].iValue));
}
iResultSize=iTrimSize;
iWriter->Write(iTrimSize,gba_sleep_patch_bin,gba_sleep_patch_bin_size);
iResultSize=iTrimSize+gba_sleep_patch_bin_size+ACTIVATION_SIZE*iCount3;
for(u32 ii=0;ii<iCount3;ii++)
{
u32 mem[9];
mem[0]=0xE92D5003;
mem[1]=0xE1A0C000+iPatchInfo3[ii].iRa;
mem[2]=0xE1A01000+iPatchInfo3[ii].iRb;
mem[3]=0xE1A0000C;
mem[4]=0xEB000000+((0x00FFFFFF)&((iTrimSize-(iPatchInfo3[ii].iLocation+8+16))>>2));
mem[5]=0xE8BD5003;
mem[6]=0xE59F0000+(iPatchInfo3[ii].iRb<<12);
mem[7]=0xE12FFF10+iPatchInfo3[ii].iRb;
mem[8]=iPatchInfo3[ii].iAddress+0x08000000;
iWriter->Write(iPatchInfo3[ii].iLocation,(u8*)mem,sizeof(mem));
}
}