Hacking Nintendont

  • Thread starter Thread starter sabykos
  • Start date Start date
  • Views Views 10,172,984
  • Replies Replies 42,894
  • Likes Likes 194
I've been browsing through the code. Some random thoughts:
line 170 in Patch.c looks wrong to me.
Code:
if( (op & 0xFC00FFFF) == 0x3C00CC00 )  // lis rX, 0xCC00
The mask that's being used here would catch lis commands as intended, but would also include addis commands.
addis/lis command format: http://pds.twi.tudelft.nl/vakken/in101/labcourse/instruction-set/addis.html
Then on line 189 or 211 or 234, it replaces it with a lis command. One of two things should be happening here either the line should be:
Code:
if( (op & 0xFC1FFFFF) == 0x3C00CC00 )  // lis rX, 0xCC00
which would only match lis commands and not addis commands, or you can keep the current if command, but you'd need to track the src register and reuse it in the 3 replace lines.
Code:
        u32 LISReg=-1;
        u32 ADDISReg=-1;
        u32 LISOff=-1;
 
        for( i=0; i < Length; i+=4 )
        {
                u32 op = read32( (u32)dst + i );
                           
                if( (op & 0xFC00FFFF) == 0x3C00CC00 )  // addis rX,rY, 0xCC00
                {
                        LISReg = (op & 0x3E00000) >> 21;
                        ADDISReg = (op & 0x1F0000) >> 16;
                        LISOff = (u32)dst + i;
                }
...
                        if( src == LISReg )
                        {
                                write32( (u32)LISOff, (LISReg<<21) | (ADDISReg<<16) | 0x3C00CD00 );      // Patch to: addis rX,rY, 0xCD00

Other thoughts:
This chunk is concerning.
Code:
/*
        This offset gets randomly overwritten, this workaround fixes that problem.
*/
void Patch31A0( void )
Memory locations being written to randomly are not good. The patch which copies the instructions out of the randomly overwritten area to another location and patching in a couple of branches before and after might work in some cases, but if the code being used there is jumped to from other locations this can cause problems, as it would if the data being used there is accessed as part of some kind of structure used in the code that can't be broken up. The underlying issue that is causing 31a0 to be overwritten needs to be found and fixed.

It's interesting that there are 13 DSP hashes listed, but only profiles/lengths for the first 7. Where did the hashes come from, and why weren't the profiles and lengths generated when the hashes were? It's probably possible to auto generate hashes from dol files of images that are reporting unknown DSPs, if somewhat inefficiently, with a nested for loop of starting offsets and lengths to find the missing data for the other DSPs.

The method actually used for determining a match for patching is pretty interesting. The profile for each pattern lists how many branch&links, branches, moves, loads and stores there are inside a single function. I'd be interested to know how accurate this method is. Does it generate any false positives?
 
I've been browsing through the code. Some random thoughts:
line 170 in Patch.c looks wrong to me.
Code:
if( (op & 0xFC00FFFF) == 0x3C00CC00 )  // lis rX, 0xCC00
The mask that's being used here would catch lis commands as intended, but would also include addis commands.
addis/lis command format: http://pds.twi.tudelft.nl/vakken/in101/labcourse/instruction-set/addis.html
Then on line 189 or 211 or 234, it replaces it with a lis command. One of two things should be happening here either the line should be:
Code:
if( (op & 0xFC1FFFFF) == 0x3C00CC00 )  // lis rX, 0xCC00
which would only match lis commands and not addis commands, or you can keep the current if command, but you'd need to track the src register and reuse it in the 3 replace lines.
Code:
        u32 LISReg=-1;
        u32 ADDISReg=-1;
        u32 LISOff=-1;
 
        for( i=0; i < Length; i+=4 )
        {
                u32 op = read32( (u32)dst + i );
                         
                if( (op & 0xFC00FFFF) == 0x3C00CC00 )  // addis rX,rY, 0xCC00
                {
                        LISReg = (op & 0x3E00000) >> 21;
                        ADDISReg = (op & 0x1F0000) >> 16;
                        LISOff = (u32)dst + i;
                }
...
                        if( src == LISReg )
                        {
                                write32( (u32)LISOff, (LISReg<<21) | (ADDISReg<<16) | 0x3C00CD00 );      // Patch to: addis rX,rY, 0xCD00

Other thoughts:
This chunk is concerning.
Code:
/*
        This offset gets randomly overwritten, this workaround fixes that problem.
*/
void Patch31A0( void )
Memory locations being written to randomly are not good. The patch which copies the instructions out of the randomly overwritten area to another location and patching in a couple of branches before and after might work in some cases, but if the code being used there is jumped to from other locations this can cause problems, as it would if the data being used there is accessed as part of some kind of structure used in the code that can't be broken up. The underlying issue that is causing 31a0 to be overwritten needs to be found and fixed.

It's interesting that there are 13 DSP hashes listed, but only profiles/lengths for the first 7. Where did the hashes come from, and why weren't the profiles and lengths generated when the hashes were? It's probably possible to auto generate hashes from dol files of images that are reporting unknown DSPs, if somewhat inefficiently, with a nested for loop of starting offsets and lengths to find the missing data for the other DSPs.

The method actually used for determining a match for patching is pretty interesting. The profile for each pattern lists how many branch&links, branches, moves, loads and stores there are inside a single function. I'd be interested to know how accurate this method is. Does it generate any false positives?
You make some interesting points here. Have you tried making a build with some of those potential fixes and seeing if it improves compatibility anywhere?
 
  • Like
Reactions: Bentonoise7
You make some interesting points here. Have you tried making a build with some of those potential fixes and seeing if it improves compatibility anywhere?
I'm afraid I'm currently feeling a bit lazy. Right now, it's more interesting to just look at how the code's supposed to work and see if anything jumps out. Looking at the lis/addis issue a little more, I'd say it's unlikely to be causing any errors, because using addis with that specific value (0xCC00) with anything other than r0 (lis) is unlikely to occur. Rather than testing for improvement, it's probably enough to just test to see if any of those occurrences are even being patched and throwing it out to the log. Something like:
Code:
if(( (op & 0xFC00FFFF) == 0x3C00CC00 )  && (op & 0x1F0000 != 0))
dbgprintf("Found addis 0xCC00: :0x%p\n", dst+i );
 
Well i think that anything that can be improved should be improved.
The loader is of almost no importance, but that's what everyone keeps making suggestions about. Probably because it's a lot easier to work on a loader than a kernel.

If you want to make contributions, I strongly suggest that you look at the kernel code. Keep in mind that a lot of functions aren't available there, although there are alternatives.
 
  • Like
Reactions: MassiveRican
I sadly, doesn't know anything about the loader or the Kernel, i merely wanted to point out, that if someone can do Anything, why not do it?
Except of course, if it takes a lot of times, and makes no sense at all.

I see you doing a lot of Clean up, which in itself, doesn't do much (i think), but as it can be done to make it easier to read and stuff like that, why not do it if you got the time.
 
Hi. I would like to know if there is a Forwarder for Nintendont and if not could someone please make one? it would be great to have one! Thank you;)
 
Actually, the point he's making there DOES refer to code in the kernel in Patch.c (although I haven't really looked at the code to see when or where exactly it's dealing with PPC assembly)

My first impression is that it's patching to change memory addresses that are different in Wii and GC mode and hard coded 32-bit values like that can be loaded a few ways.
Code:
lis rX value@h
ori rX value@l
 
lis rX value@h
addi rX value@l
 
li rX value@l
addis rX value@h
 
li rX value@l
oris rX value@h
and I can see how the addis might be included with the intention of covering that possibility but in THAT case it would be missing the oris option to load the @high .

Both of those would probably only be used, though, if the programmer were to manually, specifically do something like
Code:
address = offset + 0xcc000000;
or
address = offset | 0xcc000000;
since I would assume that such low level things would have been taken care of in the SDK and be pretty consistent and predictable across most games and a compiler would be more likely to use the lis command for the @high portion of the value because the @low pcould be taken care of as part of the load and store commands for someting like
Code:
*(u32*)0xcc001234 = value;


Yes, it could be good to do put a little debug there first to see how many false positives, if any, are being caught by that. If it is catching "false positives" them and you think it's also meant to catch the other of addis commands, it would seem to me that it should both catch oris commands as well AND preserve the source register number when patching.


anyway, that's my 2 cents worth.
Either way, while speculation is interesting, tests can be more effective. ;)
 
Actually, the point he's making there DOES refer to code in the kernel in Patch.c (although I haven't really looked at the code to see when or where exactly it's dealing with PPC assembly)

My first impression is that it's patching to change memory addresses that are different in Wii and GC mode and hard coded 32-bit values like that can be loaded a few ways.
Exactly. The point of the code in this area is to fix the Audio Interface as implied by the function name. Check out this page:
http://wiibrew.org/wiki/Hardware/Audio_Interface
According to this page, the audio interface registers changed from 0xCC006C00 in the Gamecube to 0xCD006C00 in the Wii (it actually says 0xCD806C00 in one place and 0xCD006C00 in another. I'm assuming the second is correct, as that's what the patch code in Nintendont is doing).

Code:
lis rX value@h
ori rX value@l
 
lis rX value@h
addi rX value@l
 
li rX value@l
addis rX value@h
 
li rX value@l
oris rX value@h
and I can see how the addis might be included with the intention of covering that possibility but in THAT case it would be missing the oris option to load the @high .
Right idea. It currently only looks for lis rX 0xCC00h. If it then finds one of addi rY, rX 0x6Czz, lwz rY rX 0x6Czz or swz rY rX 0x6Czz, then it goes back to the original lis rX 0xCC00 is switched to a lis rX 0xCD00.

Both of those would probably only be used, though, if the programmer were to manually, specifically do something like
Code:
address = offset + 0xcc000000;
or
address = offset | 0xcc000000;
since I would assume that such low level things would have been taken care of in the SDK and be pretty consistent and predictable across most games and a compiler would be more likely to use the lis command for the @high portion of the value because the @low pcould be taken care of as part of the load and store commands for someting like
Code:
*(u32*)0xcc001234 = value;
Agreed. That's why I replied that I didn't think the case was likely.
Edit: I'd say line 176 should also be changed
Code:
 if( (op & 0xFC000000) == 0x38000000 )  // li rX, x
to
if( ((op & 0xFC000000) == 0x38000000 ) && ((op & 0x3E00000)>>21)) // li rX, x
This would mean it would cancel looking only when 0xCC00 is overwritten. As Maxternal said though these are unlikely scenarios.
 
You can always make a test version and post it here, i'm always willing to test Nintendont releases of any kind if i'm not busy
 
You can always make a test version and post it here, i'm always willing to test Nintendont releases of any kind if i'm not busy
I'm not setup to build right now, but if someone added these 2 lines at line 170 in Patch.c and then built and ran with a few games, any instances should show up in the log.
if(( (op & 0xFC00FFFF) == 0x3C00CC00 ) && (op & 0x1F0000 != 0))
dbgprintf("Found addis 0xCC00: :0x%p\n", dst+i );
 

Site & Scene News

Popular threads in this forum