get wifi beacons going

This commit is contained in:
Arisotura 2023-06-23 20:46:31 +02:00
parent 139ab069f5
commit 49fd6b1576
2 changed files with 533 additions and 194 deletions

View File

@ -391,10 +391,12 @@ void Reset()
{
u32 gamecode = *(u32*)&NDSCart::CartROM[0xC];
gamecode &= 0xFFFFFF;
if (gamecode == 0x4D5341) // ASMx / Super Mario 64 DS
if (gamecode == 0x4D5341) // ASMx / Super Mario 64 DS
Version = 1;
else if (gamecode == 0x595241) // ARYx / Rayman DS
Version = 2;
else if (gamecode == 0x475541) // AUGx / Need for Speed Underground
Version = 2;
}
NDS::ScheduleEvent(NDS::Event_HLE_SoundCmd, true, 174592, Process, 1);

View File

@ -21,6 +21,7 @@
#include "../HLE.h"
#include "../FIFO.h"
#include "../SPI.h"
#include "../GPU.h"
#include "IPC.h"
#include "Wifi.h"
@ -43,13 +44,26 @@ struct IPCReply
FIFO<IPCReply, 16> IPCReplyQueue;
s32 TimerError;
u32 SharedMem[2];
u8 MAC[6];
u16 TXSeqNo;
u64 Timestamp;
u16 Channel;
u8 RXBuffer[2048];
u16 BeaconCount;
u16 BeaconInterval;
u8 BeaconFrame[1024];
u8* BeaconTagDD;
u16 HostScanCount;
u32 HostScanBuffer;
void ScheduleTimer(bool first);
void WifiIPCReply(u16 cmd, u16 status, int numextra=0, u16* extra=nullptr);
@ -57,20 +71,271 @@ void Reset()
{
IPCReplyQueue.Clear();
TimerError = 0;
SharedMem[0] = 0;
SharedMem[1] = 0;
u8* mac = SPI_Firmware::GetWifiMAC();
memcpy(MAC, mac, 6);
TXSeqNo = 0;
Timestamp = 0;
Channel = 0;
memset(RXBuffer, 0, sizeof(RXBuffer));
BeaconCount = 0;
BeaconInterval = 0;
memset(BeaconFrame, 0, sizeof(BeaconFrame));
BeaconTagDD = nullptr;
HostScanCount = 0;
HostScanBuffer = 0;
u16 chanmask = 0x2082;
NDS::ARM7Write16(0x027FFCFA, chanmask);
}
void SendHostBeacon()
{printf("HOST BEACON\n");
*(u16*)&BeaconFrame[12 + 22] = (TXSeqNo << 4);
TXSeqNo++;
*(u64*)&BeaconFrame[12 + 24] = Timestamp;
u16 syncval = ((GPU::VCount * 0x7F) - (Timestamp * 2)) >> 7;
*(u16*)&BeaconTagDD[8] = syncval;
Platform::MP_SendPacket(BeaconFrame, 12+*(u16*)&BeaconFrame[10], Timestamp);
}
bool ReceiveHostBeacon()
{
int rxlen = Platform::MP_RecvPacket(RXBuffer, nullptr);
if (rxlen <= (12+24)) return false;
u16 framelen = *(u16*)&RXBuffer[10];
if (framelen != rxlen-12)
{
//Log(LogLevel::Error, "bad frame length %d/%d\n", framelen, rxlen-12);
return false;
}
u16 framectl = *(u16*)&RXBuffer[12+0];
if ((framectl & 0xE7FF) != 0x0080) return false;
u8* tagpos = &RXBuffer[12+24+8+2+2];
u8* frameend = &RXBuffer[rxlen];
while (tagpos < frameend)
{
u8 tag = *tagpos++;
u8 len = *tagpos++;
if (tag == 0xDD)
{
u32 oui = *(u32*)tagpos;
if (oui == 0x00BF0900)
return true;
}
tagpos += len;
}
return false;
}
void HostScan()
{
bool res = ReceiveHostBeacon();
if (res)
{
// fill beacon info buffer
u16 buf_len = 0x41;
u8* dd_offset = nullptr;
u16 dd_len = 0;
NDS::ARM7Write16(HostScanBuffer+0x02, RXBuffer[12]); // CHECKME
NDS::ARM7Write16(HostScanBuffer+0x04, RXBuffer[12+10]);
NDS::ARM7Write16(HostScanBuffer+0x06, RXBuffer[12+12]);
NDS::ARM7Write16(HostScanBuffer+0x08, RXBuffer[12+14]);
NDS::ARM7Write16(HostScanBuffer+0x2C, RXBuffer[12+24+8+2]);
NDS::ARM7Write16(HostScanBuffer+0x32, RXBuffer[12+24+8]);
NDS::ARM7Write16(HostScanBuffer+0x0A, 0);
NDS::ARM7Write16(HostScanBuffer+0x34, 0);
NDS::ARM7Write16(HostScanBuffer+0x38, 0);
NDS::ARM7Write16(HostScanBuffer+0x3C, 0);
NDS::ARM7Write16(HostScanBuffer+0x3E, 0);
u16 rxlen = *(u16*)&RXBuffer[10] + 12;
u8* tagpos = &RXBuffer[12+24+8+2+2];
u8* frameend = &RXBuffer[rxlen];
while (tagpos < frameend)
{
u8 tag = *tagpos++;
u8 len = *tagpos++;
switch (tag)
{
case 0x00: // SSID
{
u8 ssidlen = len;
if (ssidlen > 0x20) ssidlen = 0x20;
NDS::ARM7Write16(HostScanBuffer+0x0A, ssidlen);
for (int i = 0; i < ssidlen; i++)
NDS::ARM7Write8(HostScanBuffer+0xC+i, tagpos[i]);
}
break;
case 0x01: // supported rates
{
u16 mask1 = 0;
u16 mask2 = 0;
for (int i = 0; i < len; i++)
{
u8 val = tagpos[i];
u16 bit;
switch (val & 0x7F)
{
case 0x02: bit = (1<<0); break;
case 0x04: bit = (1<<1); break;
case 0x0B: bit = (1<<2); break;
case 0x0C: bit = (1<<3); break;
case 0x12: bit = (1<<4); break;
case 0x16: bit = (1<<5); break;
case 0x30: bit = (1<<6); break;
case 0x48: bit = (1<<7); break;
case 0x60: bit = (1<<8); break;
case 0x6C: bit = (1<<9); break;
default: bit = (1<<15); break;
}
if (val & 0x80) mask1 |= bit;
mask2 |= bit;
}
NDS::ARM7Write16(HostScanBuffer+0x2E, mask1);
NDS::ARM7Write16(HostScanBuffer+0x30, mask2);
}
break;
case 0x03: // channel
{
if (len != 1) break;
NDS::ARM7Write16(HostScanBuffer+0x36, tagpos[0]);
}
break;
case 0x04: // CF parameters
{
if (len != 6) break;
NDS::ARM7Write16(HostScanBuffer+0x38, tagpos[1]);
}
break;
case 0x05: // TIM
{
if (len < 4) break;
NDS::ARM7Write16(HostScanBuffer+0x34, tagpos[1]);
}
break;
case 0xDD: // tag DD
{
// TODO count bad tag DD's
if (len < 8) break;
if (*(u32*)&tagpos[0] != 0x00BF0900) break;
dd_offset = &tagpos[8];
dd_len = len - 8;
NDS::ARM7Write16(HostScanBuffer+0x3C, dd_len);
for (int i = 0; i < dd_len; i++)
NDS::ARM7Write8(HostScanBuffer+0x40+i, tagpos[8+i]);
buf_len += dd_len;
}
break;
}
tagpos += len;
}
NDS::ARM7Write16(HostScanBuffer+0x00, (buf_len >> 1));
// fill reply buffer
WifiIPCReply(0xA, 0, dd_len, (u16*)dd_offset);
}
else if (HostScanCount == 0)
{
WifiIPCReply(0xA, 0);
}
}
void MSTimer(u32 param)
{
u16 status = NDS::ARM7Read16(SharedMem[1]);
Timestamp += 1024;
switch (status)
{
case 5: // scanning for host beacons
{
if (HostScanCount > 0)
{
HostScanCount--;
HostScan();
}
if (HostScanCount > 0)
ScheduleTimer(false);
}
break;
case 7:
case 9: // host comm
{
BeaconCount++;
if (BeaconCount >= BeaconInterval)
{
BeaconCount = 0;
SendHostBeacon();
}
ScheduleTimer(false);
}
break;
}
}
void ScheduleTimer(bool first)
{
if (first)
{
NDS::CancelEvent(NDS::Event_Wifi);
TimerError = 0;
}
s64 cycles = 33513982LL * 1024LL;
cycles -= TimerError;
s64 delay = (cycles + 999999LL) / 1000000LL;
TimerError = (s32)((delay * 1000000LL) - cycles);
NDS::ScheduleEvent(NDS::Event_Wifi, !first, (s32)delay, MSTimer, 0);
}
void StartHostComm()
{
memset(BeaconFrame, 0xFF, sizeof(BeaconFrame));
@ -84,6 +349,7 @@ void StartHostComm()
u32 gameid = NDS::ARM7Read32(paramblock + 0x08);
u16 streamcode = NDS::ARM7Read16(paramblock + 0x0C);
BeaconCount = 0;
BeaconInterval = NDS::ARM7Read16(paramblock + 0x18);
u16 channel = NDS::ARM7Read16(paramblock + 0x32);
@ -142,6 +408,7 @@ void StartHostComm()
*ptr++ = 0x00;
// tag DD
BeaconTagDD = ptr;
*ptr++ = 0xDD; *ptr++ = (0x18 + dd_len);
*(u32*)ptr = 0x00BF0900; ptr += 4;
*(u16*)ptr = 0x000A; ptr += 2; // stepping (checkme)
@ -167,7 +434,7 @@ void StartHostComm()
len += 4;
// frame length
*(u16*&)BeaconFrame[0xA] = len;
*(u16*)&BeaconFrame[0xA] = len;
}
@ -215,6 +482,38 @@ void WifiIPCReply(u16 cmd, u16 status, int numextra, u16* extra)
NDS::ARM7Write16(replybuf+0x2E, extra[2]);
}
}
else if (cmd == 0xA)
{
if (numextra > 0)
{
NDS::ARM7Write16(replybuf+0x8, 5);
NDS::ARM7Write16(replybuf+0x10, Channel);
NDS::ARM7Write16(replybuf+0x12, 0);
// source MAC
NDS::ARM7Write16(replybuf+0xA, *(u16*)&RXBuffer[12+10]);
NDS::ARM7Write16(replybuf+0xC, *(u16*)&RXBuffer[12+12]);
NDS::ARM7Write16(replybuf+0xE, *(u16*)&RXBuffer[12+14]);
NDS::ARM7Write16(replybuf+0x14, 0); // ???
NDS::ARM7Write16(replybuf+0x36, numextra);
for (int i = 0; i < numextra; i+=2)
{
NDS::ARM7Write16(replybuf+0x38+i, extra[i>>1]);
}
}
else
{
NDS::ARM7Write16(replybuf+0x8, 4);
NDS::ARM7Write16(replybuf+0x10, Channel);
NDS::ARM7Write16(replybuf+0x12, 0);
}
}
else if (cmd == 0xE && status == 0)
{
NDS::ARM7Write16(replybuf+0x04, 0xA);
}
else
{
for (int i = 0; i < numextra; i++)
@ -232,224 +531,262 @@ void WifiIPCReply(u16 cmd, u16 status, int numextra, u16* extra)
void OnIPCRequest(u32 addr)
{
u16 cmd = NDS::ARM7Read16(addr);
cmd &= ~0x8000;
/*printf("WIFI HLE: cmd %04X\n", cmd);
for (u32 i = 0; i < 16; i++)
if (cmd < 0x2E)
{
for (u32 j = 0; j < 16; j+=4)
NDS::ARM7Write32(SharedMem[1]+0x4, 1);
NDS::ARM7Write16(SharedMem[1]+0x2, cmd);
switch (cmd)
{
u32 varp = NDS::ARM7Read32(addr+(i*16)+j);
printf("%08X ", varp);
}
printf("\n");
}*/
switch (cmd)
{
case 0x0: // init
{
SharedMem[0] = NDS::ARM7Read32(addr+0x4);
SharedMem[1] = NDS::ARM7Read32(addr+0x8);
u32 respbuf = NDS::ARM7Read32(addr+0xC);
NDS::ARM7Write32(SharedMem[0], SharedMem[1]);
NDS::ARM7Write32(SharedMem[0]+0x8, respbuf);
// TODO init the sharedmem buffers
// TODO other shito too!!
NDS::ARM7Write16(SharedMem[1], 2);
WifiIPCReply(0x0, 0);
}
break;
case 0x2: // deinit
{
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status == 2)
case 0x0: // init
{
NDS::ARM7Write16(SharedMem[1], 0);
WifiIPCReply(0x2, 0);
SharedMem[0] = NDS::ARM7Read32(addr+0x4);
SharedMem[1] = NDS::ARM7Read32(addr+0x8);
u32 respbuf = NDS::ARM7Read32(addr+0xC);
NDS::ARM7Write32(SharedMem[0], SharedMem[1]);
NDS::ARM7Write32(SharedMem[0]+0x8, respbuf);
// TODO init the sharedmem buffers
// TODO other shito too!!
NDS::ARM7Write16(SharedMem[1], 2);
Platform::MP_Begin();
WifiIPCReply(0x0, 0);
}
else
break;
case 0x2: // deinit
{
WifiIPCReply(0x2, 3);
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status == 2)
{
NDS::ARM7Write16(SharedMem[1], 0);
Platform::MP_End();
WifiIPCReply(0x2, 0);
}
else
{
WifiIPCReply(0x2, 3);
}
}
}
break;
break;
case 0x3: // enable
{
SharedMem[0] = NDS::ARM7Read32(addr+0x4);
SharedMem[1] = NDS::ARM7Read32(addr+0x8);
u32 respbuf = NDS::ARM7Read32(addr+0xC);
NDS::ARM7Write32(SharedMem[0], SharedMem[1]);
NDS::ARM7Write32(SharedMem[0]+0x8, respbuf);
// TODO init the sharedmem buffers
NDS::ARM7Write16(SharedMem[1], 1);
WifiIPCReply(0x3, 0);
}
break;
case 0x4: // disable
{
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status == 1)
case 0x3: // enable
{
NDS::ARM7Write16(SharedMem[1], 0);
WifiIPCReply(0x4, 0);
SharedMem[0] = NDS::ARM7Read32(addr+0x4);
SharedMem[1] = NDS::ARM7Read32(addr+0x8);
u32 respbuf = NDS::ARM7Read32(addr+0xC);
NDS::ARM7Write32(SharedMem[0], SharedMem[1]);
NDS::ARM7Write32(SharedMem[0]+0x8, respbuf);
// TODO init the sharedmem buffers
NDS::ARM7Write16(SharedMem[1], 1);
WifiIPCReply(0x3, 0);
}
else
break;
case 0x4: // disable
{
WifiIPCReply(0x4, 3);
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status == 1)
{
NDS::ARM7Write16(SharedMem[1], 0);
WifiIPCReply(0x4, 0);
}
else
{
WifiIPCReply(0x4, 3);
}
}
}
break;
break;
case 0x7: // set host param
{
// PARAM BLOCK FORMAT
// offset size desc.
// 00 4 tag DD: pointer to extra data
// 04 2 tag DD: length of extra data (tag DD length minus 0x18)
// 06 2
// 08 6 SSID bytes 0..5 (SSID is 32 bytes, rest is all zeroes)
// 08 4 tag DD: game ID
// 0C 2 tag DD: stream code
// 0E 2 tag DD: beacon type bit0
// 10 2 ???
// 12 2 tag DD: beacon type bit1
// 14 2 tag DD: beacon type bit2
// 16 2 tag DD: beacon type bit3
// 18 2 beacon interval
// 32 2 channel #
// 34 2 tag DD: CMD data length
// 36 2 tag DD: REPLY data length
// for now, the param block is just copied to sharedmem
u32 paramblock = NDS::ARM7Read32(addr+0x4);
u32 dst = SharedMem[1] + 0xE8;
for (u32 i = 0; i < 0x40; i+=4)
case 0x7: // set host param
{
NDS::ARM7Write32(dst+i, NDS::ARM7Read32(paramblock+i));
// PARAM BLOCK FORMAT
// offset size desc.
// 00 4 tag DD: pointer to extra data
// 04 2 tag DD: length of extra data (tag DD length minus 0x18)
// 06 2
// 08 6 SSID bytes 0..5 (SSID is 32 bytes, rest is all zeroes)
// 08 4 tag DD: game ID
// 0C 2 tag DD: stream code
// 0E 2 tag DD: beacon type bit0
// 10 2 ???
// 12 2 tag DD: beacon type bit1
// 14 2 tag DD: beacon type bit2
// 16 2 tag DD: beacon type bit3
// 18 2 beacon interval
// 32 2 channel #
// 34 2 tag DD: CMD data length
// 36 2 tag DD: REPLY data length
// for now, the param block is just copied to sharedmem
u32 paramblock = NDS::ARM7Read32(addr+0x4);
u32 dst = SharedMem[1] + 0xE8;
for (u32 i = 0; i < 0x40; i+=4)
{
NDS::ARM7Write32(dst+i, NDS::ARM7Read32(paramblock+i));
}
WifiIPCReply(0x7, 0);
}
break;
WifiIPCReply(0x7, 0);
}
break;
case 0x8: // start host comm
{
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status != 2)
case 0x8: // start host comm
{
u16 ext = 0;
WifiIPCReply(0x8, 3, 1, &ext);
break;
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status != 2)
{
u16 ext = 0;
WifiIPCReply(0x8, 3, 1, &ext);
break;
}
StartHostComm();
NDS::ARM7Write16(SharedMem[1], 7);
u16 extra[3];
extra[0] = 0;
extra[1] = NDS::ARM7Read16(SharedMem[1]+0xE8+0x34);
extra[2] = NDS::ARM7Read16(SharedMem[1]+0xE8+0x36);
WifiIPCReply(0x8, 0, 3, extra);
NDS::ARM7Write16(SharedMem[1]+0xC2, 1);
ScheduleTimer(true);
}
break;
StartHostComm();
NDS::ARM7Write16(SharedMem[1], 7);
u16 extra[3];
extra[0] = 0;
extra[1] = NDS::ARM7Read16(SharedMem[1]+0xE8+0x34);
extra[2] = NDS::ARM7Read16(SharedMem[1]+0xE8+0x36);
WifiIPCReply(0x8, 0, 3, extra);
NDS::ARM7Write16(SharedMem[1]+0xC2, 1);
}
break;
case 0xA: // start host scan
{
// scan for viable host beacons
// timeout: ~20480*64 cycles (with value 0x22)
//
// COMMAND BUFFER
// offset size desc.
// 04 4 pointer to beacon data buffer
// 08 2 timeout in milliseconds
// 0A 6 desired source MAC? seen set to FFFFFFFFFFFF
//
// REPLY BUFFER
// offset size desc.
// 08 2 ??? seen set to 4
// 0A 6 source MAC from beacon (zero if none)
// 10 ? channel
// 36 2 length of tag DD data (minus first 8 bytes) (zero if none)
// 38 X tag DD data (minus first 8 bytes)
//
// BEACON DATA BUFFER
// offset size desc.
// 00 2 buffer length in halfwords ((tag DD length - 8 + 0x41) >> 1)
// 02 2 frame control? AND 0xFF -- 0080
// 04 6 source MAC
// 0A 2 SSID length (0 if none)
// 0C 32 SSID (if present)
// 2C 2 beacon capability field
// 2E 2 supported rate bitmask (when bit7=1)
// bit0 = 82 / 1Mbps
// bit1 = 84 / 2Mbps
// bit2 = 8B
// bit3 = 8C
// bit4 = 92
// bit5 = 96
// bit6 = B0
// bit7 = C8
// bit8 = E0
// bit9 = EC
// bit15 = any other rate (with bit7=1)
// 30 2 supported rate bitmask
// bit0 = 02/82 / 1Mbps
// bit1 = 04/84 / 2Mbps
// bit2 = 0B/8B
// bit3 = 0C/8C
// bit4 = 12/92
// bit5 = 16/96
// bit6 = 30/B0
// bit7 = 48/C8
// bit8 = 60/E0
// bit9 = 6C/EC
// bit15 = any other rate
// 32 2 beacon interval
// 34 2 beacon TIM period field (0 if none)
// 36 2 beacon channel field
// 38 2 beacon CF period field (0 if none)
// 3A 2 ??
// 3C 2 length of tag DD data (minus first 8 bytes)
// 3E 2 number of bad tag DD's (when first 4 bytes are =/= 00:09:BF:00)
// 40 X tag DD data (minus first 8 bytes)
}
break;
case 0x1E: // measure channel
{
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status != 2)
case 0xA: // start host scan
{
WifiIPCReply(0x1E, 3);
break;
// scan for viable host beacons
// timeout: ~20480*64 cycles (with value 0x22)
//
// COMMAND BUFFER
// offset size desc.
// 02 2 channel
// 04 4 pointer to beacon data buffer
// 08 2 timeout in milliseconds
// 0A 6 desired source MAC? seen set to FFFFFFFFFFFF
//
// REPLY BUFFER
// offset size desc.
// 08 2 5 to indicate a new beacon, 4 otherwise
// 0A 6 source MAC from beacon (zero if none)
// 10 ? channel
// 36 2 length of tag DD data (minus first 8 bytes) (zero if none)
// 38 X tag DD data (minus first 8 bytes)
//
// BEACON DATA BUFFER
// offset size desc.
// 00 2 buffer length in halfwords ((tag DD length - 8 + 0x41) >> 1)
// 02 2 frame control? AND 0xFF -- 0080
// 04 6 source MAC
// 0A 2 SSID length (0 if none)
// 0C 32 SSID (if present)
// 2C 2 beacon capability field
// 2E 2 supported rate bitmask (when bit7=1)
// bit0 = 82 / 1Mbps
// bit1 = 84 / 2Mbps
// bit2 = 8B
// bit3 = 8C
// bit4 = 92
// bit5 = 96
// bit6 = B0
// bit7 = C8
// bit8 = E0
// bit9 = EC
// bit15 = any other rate (with bit7=1)
// 30 2 supported rate bitmask
// bit0 = 02/82 / 1Mbps
// bit1 = 04/84 / 2Mbps
// bit2 = 0B/8B
// bit3 = 0C/8C
// bit4 = 12/92
// bit5 = 16/96
// bit6 = 30/B0
// bit7 = 48/C8
// bit8 = 60/E0
// bit9 = 6C/EC
// bit15 = any other rate
// 32 2 beacon interval
// 34 2 beacon TIM period field (0 if none)
// 36 2 beacon channel field
// 38 2 beacon CF period field (0 if none)
// 3A 2 ??
// 3C 2 length of tag DD data (minus first 8 bytes)
// 3E 2 number of bad tag DD's (when first 4 bytes are =/= 00:09:BF:00)
// 40 X tag DD data (minus first 8 bytes)
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status != 2 && status != 3 && status != 5)
{
u16 ext = 4;
WifiIPCReply(0xA, 3, 1, &ext);
break;
}
// TODO: check incoming parameters
Channel = NDS::ARM7Read16(addr+0x2);
HostScanCount = NDS::ARM7Read16(addr+0x8);
HostScanBuffer = NDS::ARM7Read32(addr+0x4);
NDS::ARM7Write16(SharedMem[1], 5);
ScheduleTimer(true);
}
break;
u16 chan = NDS::ARM7Read16(addr+0x6);
u16 extra[2] = {chan, 1};
case 0xE: // start local MP
{
// TODO!!
WifiIPCReply(0x1E, 0, 2, extra);
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status == 8)
NDS::ARM7Write16(SharedMem[1], 10);
else if (status == 7)
NDS::ARM7Write16(SharedMem[1], 9);
printf("WIFI HLE: START MP\n");
WifiIPCReply(0xE, 0);
}
break;
case 0x1E: // measure channel
{
u16 status = NDS::ARM7Read16(SharedMem[1]);
if (status != 2)
{
WifiIPCReply(0x1E, 3);
break;
}
u16 chan = NDS::ARM7Read16(addr+0x6);
u16 extra[2] = {chan, 1};
WifiIPCReply(0x1E, 0, 2, extra);
}
break;
default:
printf("WIFI HLE: unknown command %04X\n", cmd);
break;
}
break;
default:
printf("WIFI HLE: unknown command %04X\n", cmd);
break;
NDS::ARM7Write32(SharedMem[1]+0x4, 0);
}
cmd |= 0x8000;
NDS::ARM7Write16(addr, cmd);
}
}