Nintendo wifi config utility
The Nintendo wifi config utility is an utility that's provided with most wifi-compliant games.
It allows the user to easily connect to an access point.
The goal is to get this utility to recognize SoftAP (the software access point inside DeSmuME).
(Here I'm using the utility of Mario Kart DS. That's why the addresses are located into ARM7-mapped VRAM.
The game seems to use so much memory that it needs to copy its ARM7 code to ARM7-mapped VRAM!
On other games the addresses should be something standard like WRAM or main RAM.)
The utility doesn't rely on beacons to find access points. It does active scanning.
It sends probe request frames periodically and examinates the probe responses received from access points.
The problem is that, on DeSmuME, that util doesn't seem to like the probe responses generated by SoftAP.
It always overwrites the first few bytes of the response. It seems to want to read the packet header, but
instead it'll read at the end of the packet. The possible causes of that problem are:
- incorrect wifi emulation
- unknown wifi mode
- CPU bug
After investigating, I found that the util wants to read two values in the RX header (frame type and
transfer rate), but instead of reading them at <packet_start> and <packet_start + 0x6>, it reads them at
<u16[packet_start+0x8] + 0xC> and <u16[packet_start+0x8] + 0x12>.
(Note that packet_start is 04804BFC)
I believe the issue is most likely a CPU bug. Because I've tested two homebrews that use the wifi lib by sgstair
(one of them is a modified version of the apConnect example provided with devKitPro, the other one was provided
with the dswifi lib I think), and sometimes they'll hang while waiting for incoming packets. I tested them on my DS
and they always worked without hanging. That's why I believe there's a common bug between these homebrews and the Nintendo
wifi config util. That common bug is likely a CPU bug, seeing the consequences:
- with the homebrews, sometimes, hanging, and possibly screens turning white
- with the util, values read from right after the packet body (they should be read from the RX header)
Here is the packet check/read routine used by the util:
Note: only the interesting part is shown here, ie from 0600CC2C
to 0600CC98, as well as the routines called within that block.
; r0 = 4804C04
; r7 = 5FE
E1D020B0 | ldrh r2, [r0]
E0820087 | add r0, r2, r7, lsl 1
E280000F | add r0, r0, F
E1A00120 | mov r0, r0, lsr 2
E1A07080 | mov r7, r0, lsl 1
E3570EFB | cmp r7, FB0
21DA09BA | ldrhcs r0, [r10, +9A]
204770A0 | subcs r7, r7, r0, lsr 1
E59F02E8 | ldr r0, [pc, +2E8]
E1520000 | cmp r2, r0
9A000006 | bls 0600CC74
E59F02E0 | ldr r0, [pc, +2E0]
E1C800B0 | strh r0, [r8, 0]
E1A07006 | mov r7, r6
E1DA0BB4 | ldrh r0, [r10, +BB4]
E2800001 | add r0, r0, 1
E1CA0BB4 | strh r0, [r10, +BB4]
EA000025 | b 0600CD0C
E3550000 | cmp r5, 0
0A000023 | beq 0600CD0C
E1570006 | cmp r7, r6
0A000021 | beq 0600CD0C
E1A01087 | mov r1, r7, lsl 1
E59F02A8 | ldr r0, [pc, +2A8]
E0800087 | add r0, r0, r7
E2811512 | add r1, r1, 4800000
E2811901 | add r1, r1, 4000
E1D110B0 | ldrh r1, [r1]
r8 = frame type from packet's RX header
E1D800B0 | ldrh r0, [r8]
E200000F | and r0, r0, F
E350000C | cmp r0, C
1A00004C | bne 0600CE50
E288000C | add r0, r8, C
EBFFFE16 | bl 0600C580
E1D0B0B0 | ldrh r11, [r0]
E2880022 | add r0, r8, 22
EBFFFE13 | bl 0600C580
E1D060B0 | ldrh r6, [r0]
E1D900B0 | ldrh r0, [r9]
E1500006 | cmp r0, r6
1A000009 | bne 0600CD68
E21B0B02 | ands r0, r11, 800
0A000007 | beq 0600CD68
E59F01D0 | ldr r0, [pc, +1D0]
Translation to pseudocode:
// r0 = 0x4804C04;
// r6 = 0x61E;
// r7 = 0x5FE;
r2 = halfword[r0];
r0 = (r2 + (r7 << 1)); // 0x30 + 0xBFC = 0xC2C
r0 += 0xF; // 0xC2C + 0xF = 0xC3B
r0 >>= 2; // 0xC3B >> 2 = 0x30E
r7 = (r0 << 1); // 0x30E << 1 = 0x61C
if(r7 >= 0xFB0) // check if packet is too long?
r0 = halfword[r10 + 0x9A];
r7 -= (r0 >> 1);
r0 = word[pc + 0x2E8]; // 0x92C
if(r2 <= r0) // check if packet is too long? again? wtf?
r0 = word[pc + 0x2E0];
halfword[r8] = r0;
r7 = r6; // r6 = 0x61E
r0 = halfword[r10 + 0xBB4];
halfword[r10 + 0xBB4] = r0;
goto proc_0600CD0C; // the exit when the packet is too long (frame header+body length > 0x92C)
if(r5 == 0)
if(r7 == r6) // wtf? (0x61C =/= 0x61E)
r1 = (r7 << 1);
r0 = word[pc + 0x2A8];
r0 += r7;
r1 += 0x4800000;
r1 += 0x4000;
r1 = halfword[r1];
The issue mentioned above is now fixed. It was caused by packet length frame in RX header. Seems GBATek is wrong on that point.
Now another problem arises. SoftAP is now detected, but the connection test always fails with error 51300 or 51301.
After investigating a bit, I found that, for some reason, before the connection test starts, the TX location regs aren't updated and
the packet to be sent isn't copied to MAC memory. So, if an AP search was performed before performing the connection test, the packet
being sent during the connection test is the exact same packet as the one sent during AP search process, ie a probe request.
Otherwise, if no AP search was performed, nothing is being sent because the TX location regs are all set to zero.
That issue could be a CPU bug or, again, due to bad wifi emulation.
What I believe to be the packet sending routine begins at 0x6012EDC.
Let's handle the issue differently
MKDS, as well as the other WFC compliant games, contain a file named utility.bin, usually found in a folder named dwc, that seems to be the WFC config utility binary.
This binary contains a long list of filenames (FNT), then a FAT, then file data. It is some kind of archive, apparently.
Among the file data, there is graphics data, ARM assembly code, debug strings related to the Nintendo SDK APIs, and other misc stuff.
I could find addresses of wifi hardware registers inside the code. That's a good sign. That means that binary contains interesting stuff.
0x00000000 (32) - abs offset to the FNT
0x00000004 (32) - FNT size
0x00000008 (32) - abs offset to the FAT
0x0000000C (32) - FAT size
The FNT and FAT structures are actually similar to those of the NitroROM file system used in NDS ROMs.
Once the file has been split into multiple files, there are mostly graphics, sound and localization files. But there's a quite interesting one: move/child.srl
This one is... a NDS ROM, just like the .nds files. Its NDS header contains a completely valid Nintendo logo. Its title is: NINTENDO NTRJ01
Chip capacity: 256MB (!)
ARM9 ROM offset: 0x00004000
ARM9 entry point: 0x02000850
ARM9 RAM address: 0x02000000
ARM9 size: 0x00030638
ARM7 ROM offset: 0x00034800
ARM7 entry point: 0x02380000
ARM7 RAM address: 0x02380000
ARM7 size: 0x0002A140
FNT offset: 0x0005EA00
FNT size: 0x00000009
FAT offset: 0x0005EC00
FAT size: 0x00000000
This "ROM" apparently still needs to be run from a game. I tried running it alone in No$GBA, DeSmuME, iDeaS and my DS, on all of them it did nothing.
This "ROM" contains no file. But it does contain ARM9 and ARM7 code, as well as debugging strings/symbols related to the Nintendo SDK APIs.
The debug strings are part of the ARM9 and ARM7 binaries. Either they're referenced somewhere somehow, or Nintendo guys are lazy/can't configure their compiler correctly :P
The block of code at 0x0600CC2C shown above is at offset 0x22F4 in the ARM7 binary.
Would that mean that the ARM7 binary is loaded at address 0x0600A938? Edit, no. How the hell is that binary stored in memory???
Edit- the fucking binary is NOT stored in memory. move/child.srl is never read at all. The wifi code that is used is found in the game's code. So what the hell is move/child.srl for? Single-cart WFC play? AFAIK, that doesn't exist, but well...