akmenu-next/arm9/source/gbasleep.cpp
2024-10-13 02:11:12 -07:00

255 lines
9.1 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 <stdio.h>
#include <string.h>
#include "gba_sleep_patch_bin.h"
#include "gbapatcher.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));
}
}