mirror of
https://github.com/Prof9/WfcReplay.git
synced 2026-04-25 07:18:16 -05:00
Create uncompressed code as well (for non-32-bit-aligned addresses) and use shortest one.
This commit is contained in:
parent
8476680c90
commit
3511940baf
|
|
@ -20,12 +20,12 @@ Notes
|
|||
-----
|
||||
* Older versions of DeSmuME are incompatible with codes generated by WfcReplay. Use a newer version of DeSmuME based on r5047 or later, or try an alternative patching method.
|
||||
* Action Replay DS codes generated by WfcReplay may interfere with certain Action Replay DS codes from other sources, such as cheat databases.
|
||||
* Furthermore, the code may disrupt anti-anti-piracy features of older flash cards. Flash cards are not supported; only the official Action Replay DS by Datel is.
|
||||
* Furthermore, the code may disrupt anti-anti-piracy features of older flash cards. Flash cards are not supported; only the official Action Replay DS(i) by Datel is.
|
||||
* Additionally, for certain games, the code may clash with special flashcard features like soft-reset. If this happens, you will need to disable these features.
|
||||
|
||||
Credits
|
||||
=======
|
||||
**WfcReplay (c) 2014 - 2015**
|
||||
**WfcReplay (c) 2014 - 2016**
|
||||
|
||||
* Prof. 9
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace WfcReplay
|
|||
{
|
||||
class Program
|
||||
{
|
||||
static string version = "v0.6";
|
||||
static string version = "v0.7";
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
|
@ -31,6 +31,7 @@ namespace WfcReplay
|
|||
{
|
||||
bool checkMode = false;
|
||||
bool local = false;
|
||||
bool fast = false;
|
||||
|
||||
int argStart = 0;
|
||||
bool flagsLeft = true;
|
||||
|
|
@ -46,6 +47,10 @@ namespace WfcReplay
|
|||
case "-l":
|
||||
local = true;
|
||||
break;
|
||||
case "--fast":
|
||||
case "-f":
|
||||
fast = true;
|
||||
break;
|
||||
default:
|
||||
argStart--;
|
||||
flagsLeft = false;
|
||||
|
|
@ -55,10 +60,13 @@ namespace WfcReplay
|
|||
|
||||
string romPath = args[argStart];
|
||||
|
||||
if (fast) {
|
||||
Console.WriteLine("WARNING: Fast search enabled, this may not find all URLs!!");
|
||||
}
|
||||
Console.WriteLine("Loading ROM...");
|
||||
Stream rom = readFile(romPath);
|
||||
BinaryReader romReader = new BinaryReader(rom);
|
||||
string code = new Program(checkMode, local).process(romReader);
|
||||
string code = new Program(checkMode, local, fast).process(romReader);
|
||||
Console.WriteLine("Finished analyzing ROM.");
|
||||
|
||||
if (code != null)
|
||||
|
|
@ -152,10 +160,12 @@ namespace WfcReplay
|
|||
|
||||
string tempFolderPath;
|
||||
bool checkMode;
|
||||
bool fast;
|
||||
|
||||
Program(bool checkMode, bool local)
|
||||
Program(bool checkMode, bool local, bool fast)
|
||||
{
|
||||
this.checkMode = checkMode;
|
||||
this.fast = fast;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -257,9 +267,33 @@ namespace WfcReplay
|
|||
// Sort by cave length
|
||||
codeCaves.Sort((a, b) => -a[1].CompareTo(b[1]));
|
||||
|
||||
// Form code
|
||||
string code = "::Bypass HTTPS " + version + " for " + gameString + "\r\n";
|
||||
code += makeCode((uint)hookAddress, urlAddresses, codeCaves);
|
||||
// Create long and short code
|
||||
string code = "";
|
||||
|
||||
// Make an uncompressed code
|
||||
if (urlAddresses.Count <= 251) {
|
||||
code = makeCodeLong((uint)hookAddress, urlAddresses, codeCaves);
|
||||
}
|
||||
|
||||
// Check if we can make a compressed code
|
||||
bool hasMisalignedAddresses = false;
|
||||
foreach (uint urlAddress in urlAddresses) {
|
||||
if ((urlAddress & 0x3) != 0) {
|
||||
hasMisalignedAddresses = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Make a compressed code
|
||||
if (!hasMisalignedAddresses) {
|
||||
string smallCode = makeCodeSmall((uint)hookAddress, urlAddresses, codeCaves);
|
||||
if (smallCode.Length <= code.Length) {
|
||||
code = smallCode;
|
||||
}
|
||||
}
|
||||
|
||||
// Add code name
|
||||
code = "::Bypass HTTPS " + version + " for " + gameString + "\r\n" + code;
|
||||
|
||||
return code;
|
||||
}
|
||||
else
|
||||
|
|
@ -406,7 +440,7 @@ namespace WfcReplay
|
|||
result.Add(addr);
|
||||
log("\tFound URL at 0x" + addr.ToString("X8"));
|
||||
}
|
||||
reader.BaseStream.Position = pos + 4;
|
||||
reader.BaseStream.Position = pos + (this.fast ? 4 : 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -455,7 +489,120 @@ namespace WfcReplay
|
|||
return caves;
|
||||
}
|
||||
|
||||
static string makeCode(uint arm9Hook, List<uint> urlAddresses, List<List<uint>> codeCaves)
|
||||
static string makeCodeLong(uint arm9Hook, List<uint> urlAddresses, List<List<uint>> codeCaves)
|
||||
{
|
||||
string code = "";
|
||||
urlAddresses.Sort();
|
||||
|
||||
if (urlAddresses.Count > 0)
|
||||
{
|
||||
List<uint> codeParts = new List<uint>();
|
||||
// Hook check
|
||||
codeParts.Add(0x50000000 | ((arm9Hook + 4) & 0xFFFFFFF));
|
||||
codeParts.Add(0xEE070F90);
|
||||
|
||||
// Compile ASM code
|
||||
List<ushort> caveParts = new List<ushort>();
|
||||
// fspace:
|
||||
// .thumb
|
||||
// add r2,=addrPtr // get array start
|
||||
caveParts.Add(0xA20C);
|
||||
// main_loop:
|
||||
// ldmia [r2]!,r1 // get next string ptr, increment array ptr
|
||||
caveParts.Add(0xCA02);
|
||||
// mov r3,3h // start at most significant byte
|
||||
caveParts.Add(0x2303);
|
||||
// check_loop:
|
||||
// lsl r4,r4,8h // this is done 4 times, so no need to set to 0
|
||||
caveParts.Add(0x0224);
|
||||
// ldrb r5,[r1,r3] // load the byte
|
||||
caveParts.Add(0x5CCD);
|
||||
// add r4,r4,r5 // add the byte
|
||||
caveParts.Add(0x1964);
|
||||
// sub r3,1h // go to previous byte
|
||||
caveParts.Add(0x3B01);
|
||||
// bpl check_loop // if this wasn't the first byte, loop
|
||||
caveParts.Add(0xD5FA);
|
||||
// ldr r3,=2F2F3A73h // "s://"
|
||||
caveParts.Add(0x4B07);
|
||||
// cmp r3,r4 // should be "s://"
|
||||
caveParts.Add(0x42A3);
|
||||
// bne next // if not "s://", calc next string ptr
|
||||
caveParts.Add(0xD104);
|
||||
// patch_loop:
|
||||
// ldrb r4,[r1,1h] // get next char
|
||||
caveParts.Add(0x784C);
|
||||
// strb r4,[r1] // write to current
|
||||
caveParts.Add(0x700C);
|
||||
// add r1,1h // increment string ptr
|
||||
caveParts.Add(0x3101);
|
||||
// tst r4,r4 // check for zero byte
|
||||
caveParts.Add(0x4224);
|
||||
// bne patch_loop // if not zero, loop
|
||||
caveParts.Add(0xD1FA);
|
||||
// next:
|
||||
// add r1,=addrPtr_end // get array end
|
||||
caveParts.Add((ushort)(0xA104 + urlAddresses.Count));
|
||||
// cmp r1,r2 // check if at array end
|
||||
caveParts.Add(0x428A);
|
||||
// blt main_loop // if not, check next string ptr
|
||||
caveParts.Add(0xDBED);
|
||||
// end:
|
||||
// bx r15 // switch to ARM
|
||||
caveParts.Add(0x4778);
|
||||
// .arm // r0 is still zero
|
||||
// mov p15,0,c7,c0,4,r0 // wait for interrupt
|
||||
caveParts.Add(0x0F90);
|
||||
caveParts.Add(0xEE07);
|
||||
// pop r1-r4,r15 // pop registers and return
|
||||
caveParts.Add(0x803E);
|
||||
caveParts.Add(0xE8BD);
|
||||
// .pool
|
||||
caveParts.Add(0x3A73);
|
||||
caveParts.Add(0x2F2F);
|
||||
|
||||
// Add offsets
|
||||
foreach (uint urlAddress in urlAddresses) {
|
||||
caveParts.Add((ushort)((urlAddress + 4) & 0xFFFF));
|
||||
caveParts.Add((ushort)((urlAddress + 4) >> 16));
|
||||
}
|
||||
|
||||
List<uint> codeCave = codeCaves[0];
|
||||
insertCodeCave(codeParts, caveParts, codeCave);
|
||||
|
||||
codeParts.Add((arm9Hook + 4) & 0xFFFFFFF);
|
||||
// push r1-r5,r14 // push registers
|
||||
codeParts.Add(0xE92D403E);
|
||||
codeParts.Add((arm9Hook + 8) & 0xFFFFFFF);
|
||||
// blx fspace // branch to code cave
|
||||
uint opcode = 0xFA000000;
|
||||
opcode |= (uint)((codeCave[0] & 2) != 0 ? 0x01000000 : 0x00000000);
|
||||
opcode |= (uint)(-(((arm9Hook - codeCave[0]) / 4) + 4) & 0xFFFFFF);
|
||||
codeParts.Add(opcode);
|
||||
|
||||
codeParts.Add(0xD2000000);
|
||||
codeParts.Add(0x00000000);
|
||||
|
||||
bool first = true;
|
||||
foreach (uint part in codeParts) {
|
||||
if (first) {
|
||||
code += "\r\n";
|
||||
} else {
|
||||
code += " ";
|
||||
}
|
||||
code += part.ToString("X8");
|
||||
first = !first;
|
||||
}
|
||||
if (!first) {
|
||||
code += " 00000000";
|
||||
}
|
||||
code = code.Substring(2);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static string makeCodeSmall(uint arm9Hook, List<uint> urlAddresses, List<List<uint>> codeCaves)
|
||||
{
|
||||
string code = "";
|
||||
urlAddresses.Sort();
|
||||
|
|
@ -470,17 +617,29 @@ namespace WfcReplay
|
|||
// Compress offsets
|
||||
List<ushort> dataParts = new List<ushort>();
|
||||
for (int i = 0; i < urlAddresses.Count; i++) {
|
||||
// 0x24243A73 = "s://"
|
||||
// It just so happens that subtracting this from any NDS RAM address makes it negative...
|
||||
// This fact is abused to compress the addresses to check and patch. The compression works as follows:
|
||||
// - Starting offset
|
||||
// - Next 16-bit value is read in a loop
|
||||
// - If it's zero, we're done
|
||||
// - If it's positive, it's added to the current address to get the next one
|
||||
// - If it's negative, it's set as the new current address together with the next 16-bit value
|
||||
// Need to add + 4 here to skip the "http"
|
||||
uint ptr = urlAddresses[i] - 0x2F2F3A73 + 4;
|
||||
ushort upper = (ushort)(ptr >> 16);
|
||||
ushort lower = (ushort)(ptr & 0xFFFF);
|
||||
if (i == 0) {
|
||||
dataParts.Add(lower);
|
||||
dataParts.Add(upper);
|
||||
} else if (urlAddresses[i] - urlAddresses[i - 1] < 0x20000) {
|
||||
dataParts.Add((ushort)((urlAddresses[i] - urlAddresses[i - 1]) >> 2));
|
||||
} else {
|
||||
dataParts.Add(upper);
|
||||
dataParts.Add(lower);
|
||||
uint diff = urlAddresses[i] - urlAddresses[i - 1];
|
||||
if (diff < 0x20000 && (diff & 0x3) == 0) {
|
||||
dataParts.Add((ushort)((urlAddresses[i] - urlAddresses[i - 1]) >> 2));
|
||||
} else {
|
||||
dataParts.Add(upper);
|
||||
dataParts.Add(lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
dataParts.Add(0x0000);
|
||||
|
|
@ -516,7 +675,7 @@ namespace WfcReplay
|
|||
caveParts.Add(0x4224);
|
||||
// bne patch_loop // if not zero, loop
|
||||
caveParts.Add(0xD1FA);
|
||||
// next: // r0 is zero at this point
|
||||
// next: // ldsh rd,[rs,nn] isn't possible, so we need r0 here; fortunately, it should be zero at this point
|
||||
// ldsh r4,[r2,r0] // get next value (signed)
|
||||
// // if positive, offset of next ptr
|
||||
// // if negative, upper bits of next ptr
|
||||
|
|
@ -540,13 +699,13 @@ namespace WfcReplay
|
|||
// b next_add // add offset to pointer
|
||||
caveParts.Add(0xE7F9);
|
||||
// end:
|
||||
// bx r15 // switch to ARM
|
||||
// bx r15 // switch to ARM
|
||||
caveParts.Add(0x4778);
|
||||
// .arm // r0 is still zero
|
||||
// mov p15,0,c7,c0,4,r0 // wait for interrupt
|
||||
// mov p15,0,c7,c0,4,r0 // wait for interrupt
|
||||
caveParts.Add(0x0F90);
|
||||
caveParts.Add(0xEE07);
|
||||
// pop r1-r4,r15 // pop registers and return
|
||||
// pop r1-r4,r15 // pop registers and return
|
||||
caveParts.Add(0x801E);
|
||||
caveParts.Add(0xE8BD);
|
||||
// .pool
|
||||
|
|
@ -593,10 +752,10 @@ namespace WfcReplay
|
|||
}
|
||||
|
||||
codeParts.Add((arm9Hook + 4) & 0xFFFFFFF);
|
||||
// push r1-r4,r14 // push registers
|
||||
// push r1-r4,r14 // push registers
|
||||
codeParts.Add(0xE92D401E);
|
||||
codeParts.Add((arm9Hook + 8) & 0xFFFFFFF);
|
||||
// blx fspace // branch to code cave
|
||||
// blx fspace // branch to code cave
|
||||
uint opcode = 0xFA000000;
|
||||
opcode |= (uint)((asmCave[0] & 2) != 0 ? 0x01000000 : 0x00000000);
|
||||
opcode |= (uint)(-(((arm9Hook - asmCave[0]) / 4) + 4) & 0xFFFFFF);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@ Changelog
|
|||
=========
|
||||
|
||||
|
||||
WfcReplay v0.7 - 23 April 2016
|
||||
------------------------------
|
||||
Added support for games that store their URLs at memory addresses that are not 32-bit aligned. In all my testing I hadn't encountered any games that do this, but apparently they exist. Go figure.
|
||||
|
||||
Unfortunately, the custom compression scheme used in WfcReplay v0.5 onwards only works when the URL addresses are 32-bit aligned. So WfcReplay now internally generates two codes; one using the compression from v0.5 onwards (if possible!) and one without any compression, and will output the shorter one of the two.
|
||||
|
||||
* Improved support with particular NDS games.
|
||||
|
||||
|
||||
WfcReplay v0.6 - 14 August 2015
|
||||
-------------------------------
|
||||
Support for a more flexible patching system was added in this version, which increases compatibility with certain problem games that have an extreme number of URLs, such as the Japanese version of Pokémon Black 2. In fact, I'd be surprised if any other game required it...
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user