/*
    Copyright (C) 2007 Tim Seidel
    Copyright (C) 2008-2018 DeSmuME team

	This file 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 2 of the License, or
	(at your option) any later version.

	This file 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 the this software.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <assert.h>
#include "wifi.h"
#include "armcpu.h"
#include "NDSSystem.h"
#include "debug.h"
#include "utils/bits.h"
#include <driver.h>
#include <registers.h>

#ifdef HOST_WINDOWS
	#include <winsock2.h>
	#include <ws2tcpip.h>
	#define socket_t    SOCKET
	#define sockaddr_t  SOCKADDR
	#ifndef WXPORT
		#include "windriver.h"
	#endif
	#define PCAP_DEVICE_NAME description
#else
	#include <unistd.h>
	#include <stdlib.h>
	#include <string.h>
	#include <arpa/inet.h>
	#include <sys/socket.h>
	#define socket_t    int
	#define sockaddr_t  struct sockaddr
	#define closesocket close
	#define PCAP_DEVICE_NAME name
#endif

#ifndef INVALID_SOCKET
	#define INVALID_SOCKET  (socket_t)-1
#endif

#define BASEPORT 7000
#define PACKET_SIZE 65535

// Some platforms need HAVE_REMOTE to work with libpcap, but
// Apple platforms are not among them.
#ifndef __APPLE__
	#define HAVE_REMOTE
#endif

#ifdef HOST_WINDOWS
	#define WPCAP
#endif

#include <pcap.h>
typedef struct pcap pcap_t;

//sometimes this isnt defined
#ifndef PCAP_OPENFLAG_PROMISCUOUS
	#define PCAP_OPENFLAG_PROMISCUOUS 1
#endif

// PCAP_ERRBUF_SIZE should 256 bytes according to POSIX libpcap and winpcap.
// Define it if it isn't available.
#ifndef PCAP_ERRBUF_SIZE
	#define PCAP_ERRBUF_SIZE 256
#endif

LegacyWifiSFormat legacyWifiSF;

/*******************************************************************************

	WIFI TODO

	- emulate transmission delays for Beacon and Extra transfers
	- emulate delays when receiving as well (may need some queuing system)
	- take transfer rate and preamble into account
	- figure out RFSTATUS and RFPINS

 *******************************************************************************/

/*******************************************************************************

	Firmware info needed for if no firmware image is available
	see: http://www.akkit.org/info/dswifi.htm#WifiInit

	written in bytes, to avoid endianess issues

 *******************************************************************************/

u8 FW_Mac[6] 			= { 0x00, 0x09, 0xBF, 0x12, 0x34, 0x56 };

const u8 FW_WIFIInit[32] 		= { 0x02,0x00,  0x17,0x00,  0x26,0x00,  0x18,0x18,
							0x48,0x00,  0x40,0x48,  0x58,0x00,  0x42,0x00,
							0x40,0x01,  0x64,0x80,  0xE0,0xE0,  0x43,0x24,
							0x0E,0x00,  0x32,0x00,  0xF4,0x01,  0x01,0x01 };
const u8 FW_BBInit[105] 		= { 0x6D, 0x9E, 0x40, 0x05,
							0x1B, 0x6C, 0x48, 0x80,
							0x38, 0x00, 0x35, 0x07,
							0x00, 0x00, 0x00, 0x00,
							0x00, 0x00, 0x00, 0x00,
							0xb0, 0x00, 0x04, 0x01,
							0xd8, 0xff, 0xff, 0xc7,
							0xbb, 0x01, 0xb6, 0x7f,
							0x5a, 0x01, 0x3f, 0x01,
							0x3f, 0x36, 0x36, 0x00,
							0x78, 0x28, 0x55, 0x08,
							0x28, 0x16, 0x00, 0x01,
							0x0e, 0x20, 0x02, 0x98,
							0x98, 0x1f, 0x0a, 0x08,
							0x04, 0x01, 0x00, 0x00,
							0x00, 0xff, 0xff, 0xfe,
							0xfe, 0xfe, 0xfe, 0xfc,
							0xfa, 0xfa, 0xf8, 0xf8,
							0xf6, 0xa5, 0x12, 0x14,
							0x12, 0x41, 0x23, 0x03,
							0x04, 0x70, 0x35, 0x0E,
							0x16, 0x16, 0x00, 0x00,
							0x06, 0x01, 0xff, 0xfe,
							0xff, 0xff, 0x00, 0x0e,
							0x13, 0x00, 0x00, 0x28,
							0x1c
						  };
const u8 FW_RFInit[36] 		= { 0x07, 0xC0, 0x00,
							0x03, 0x9C, 0x12,
							0x28, 0x17, 0x14,
							0xba, 0xe8, 0x1a,
							0x6f, 0x45, 0x1d,
							0xfa, 0xff, 0x23,
							0x30, 0x1d, 0x24,
							0x01, 0x00, 0x28,
							0x00, 0x00, 0x2c,
							0x03, 0x9c, 0x06,
							0x22, 0x00, 0x08,
							0x6f, 0xff, 0x0d
						  };
const u8 FW_RFChannel[6*14]	= { 0x28, 0x17, 0x14,		/* Channel 1 */
							0xba, 0xe8, 0x1a,		
							0x37, 0x17, 0x14,		/* Channel 2 */
							0x46, 0x17, 0x19,		
							0x45, 0x17, 0x14,		/* Channel 3 */
							0xd1, 0x45, 0x1b,		
							0x54, 0x17, 0x14,		/* Channel 4 */
							0x5d, 0x74, 0x19,		
							0x62, 0x17, 0x14,		/* Channel 5 */
							0xe9, 0xa2, 0x1b,		
							0x71, 0x17, 0x14,		/* Channel 6 */
							0x74, 0xd1, 0x19,		
							0x80, 0x17, 0x14,		/* Channel 7 */
							0x00, 0x00, 0x18,
							0x8e, 0x17, 0x14,		/* Channel 8 */
							0x8c, 0x2e, 0x1a,
							0x9d, 0x17, 0x14,		/* Channel 9 */
							0x17, 0x5d, 0x18,
							0xab, 0x17, 0x14,		/* Channel 10 */
							0xa3, 0x8b, 0x1a,
							0xba, 0x17, 0x14,		/* Channel 11 */
							0x2f, 0xba, 0x18,
							0xc8, 0x17, 0x14,		/* Channel 12 */
							0xba, 0xe8, 0x1a,
							0xd7, 0x17, 0x14,		/* Channel 13 */
							0x46, 0x17, 0x19,
							0xfa, 0x17, 0x14,		/* Channel 14 */
							0x2f, 0xba, 0x18
						  };
const u8 FW_BBChannel[14]		= { 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,	/* channel  1- 6 */
							0xb4, 0xb4, 0xb4, 0xb4, 0xb4,	/* channel  7-10 */ 
							0xb5, 0xb5,						/* channel 11-12 */
							0xb6, 0xb6						/* channel 13-14 */
						  };

FW_WFCProfile FW_WFCProfile1 = {"SoftAP",
								"",
								"",
								"",
								"",
								"",
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								0,
								"",
								0,
								0,
								0,
								{0, 0, 0, 0, 0, 0, 0},
								0,
								{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
								{0, 0}
							   };

FW_WFCProfile FW_WFCProfile2 = {"",
								"",
								"",
								"",
								"",
								"",
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								0,
								"",
								0,
								0,
								0xFF,
								{0, 0, 0, 0, 0, 0, 0},
								0,
								{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
								{0, 0}
							   };

FW_WFCProfile FW_WFCProfile3 = {"",
								"",
								"",
								"",
								"",
								"",
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								{0, 0, 0, 0},
								0,
								"",
								0,
								0,
								0xFF,
								{0, 0, 0, 0, 0, 0, 0},
								0,
								{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
								{0, 0}
							   };

DummyPCapInterface dummyPCapInterface;
WifiHandler *wifiHandler = NULL;

/*******************************************************************************

	Logging

 *******************************************************************************/

// 0: disable logging
// 1: lowest logging, shows most important messages such as errors
// 2: low logging, shows things such as warnings
// 3: medium logging, for debugging, shows lots of stuff
// 4: high logging, for debugging, shows almost everything, may slow down
// 5: highest logging, for debugging, shows everything, may slow down a lot
#define WIFI_LOGGING_LEVEL 1

#define WIFI_LOG_USE_LOGC 0

#if (WIFI_LOGGING_LEVEL >= 1)
	#if WIFI_LOG_USE_LOGC
		#define WIFI_LOG(level, ...) if(level <= WIFI_LOGGING_LEVEL) LOGC(8, "WIFI: " __VA_ARGS__);
	#else
		#define WIFI_LOG(level, ...) if(level <= WIFI_LOGGING_LEVEL) printf("WIFI: " __VA_ARGS__);
	#endif
#else
	#define WIFI_LOG(level, ...) {}
#endif

// For debugging purposes, the results of libpcap can be written to a file.
// Note that enabling this setting can negatively affect emulation performance.
#define WIFI_SAVE_PCAP_TO_FILE 0

/*******************************************************************************

	Helpers

 *******************************************************************************/

INLINE u32 WIFI_alignedLen(u32 len)
{
	return ((len + 3) & ~3);
}

// Fast MAC compares
INLINE bool WIFI_compareMAC(const u8 *a, const u8 *b)
{
	return ((*(u32*)&a[0]) == (*(u32*)&b[0])) && ((*(u16*)&a[4]) == (*(u16*)&b[4]));
}

INLINE bool WIFI_isBroadcastMAC(const u8 *a)
{
	return ((*(u32*)&a[0]) == 0xFFFFFFFF) && ((*(u16*)&a[4]) == 0xFFFF);
}

/*******************************************************************************

	CRC32 (http://www.codeproject.com/KB/recipes/crc32_large.aspx)

 *******************************************************************************/

u32 WIFI_CRC32Table[256];

static u32 reflect(u32 ref, char ch)
{
    u32 value = 0;

    for(int i = 1; i < (ch + 1); i++)
    {
        if (ref & 1)
            value |= 1 << (ch - i);
        ref >>= 1;
    } 
	
	return value;
}

static u32 WIFI_calcCRC32(u8 *data, int len)
{
	u32 crc = 0xFFFFFFFF;

	while (len--)
        crc = (crc >> 8) ^ WIFI_CRC32Table[(crc & 0xFF) ^ *data++];

	return (crc ^ 0xFFFFFFFF);
}

static void WIFI_initCRC32Table()
{
	static bool initialized = false;
	if (initialized) return;
	initialized = true;

	u32 polynomial = 0x04C11DB7;

	for (int i = 0; i < 0x100; i++)
    {
        WIFI_CRC32Table[i] = reflect(i, 8) << 24;
        for (int j = 0; j < 8; j++)
            WIFI_CRC32Table[i] = (WIFI_CRC32Table[i] << 1) ^ (WIFI_CRC32Table[i] & (1 << 31) ? polynomial : 0);
        WIFI_CRC32Table[i] = reflect(WIFI_CRC32Table[i],  32);
    }
}

/*******************************************************************************

	RF-Chip

 *******************************************************************************/

static void WIFI_resetRF(RF2958_IOREG_MAP &rf)
{
	/* reinitialize RF chip with the default values refer RF2958 docs */
	/* CFG1 */
	rf.CFG1.IF_VGA_REG_EN = 1;
	rf.CFG1.IF_VCO_REG_EN = 1;
	rf.CFG1.RF_VCO_REG_EN = 1;
	rf.CFG1.HYBERNATE = 0;
	rf.CFG1.REF_SEL = 0;
	/* IFPLL1 */
	rf.IFPLL1.DAC = 3;
	rf.IFPLL1.P1 = 0;
	rf.IFPLL1.LD_EN1 = 0;
	rf.IFPLL1.AUTOCAL_EN1 = 0;
	rf.IFPLL1.PDP1 = 1;
	rf.IFPLL1.CPL1 = 0;
	rf.IFPLL1.LPF1 = 0;
	rf.IFPLL1.VTC_EN1 = 1;
	rf.IFPLL1.KV_EN1 = 0;
	rf.IFPLL1.PLL_EN1 = 0;
	/* IFPLL2 */
	rf.IFPLL2.IF_N = 0x22;
	/* IFPLL3 */
	rf.IFPLL3.KV_DEF1 = 8;
	rf.IFPLL3.CT_DEF1 = 7;
	rf.IFPLL3.DN1 = 0x1FF;
	/* RFPLL1 */
	rf.RFPLL1.DAC = 3;
	rf.RFPLL1.P = 0;
	rf.RFPLL1.LD_EN = 0;
	rf.RFPLL1.AUTOCAL_EN = 0;
	rf.RFPLL1.PDP = 1;
	rf.RFPLL1.CPL = 0;
	rf.RFPLL1.LPF = 0;
	rf.RFPLL1.VTC_EN = 1;
	rf.RFPLL1.KV_EN = 0;
	rf.RFPLL1.PLL_EN = 0;
	/* RFPLL2 */
	rf.RFPLL2.NUM2 = 0;
	rf.RFPLL2.N2 = 0x5E;
	/* RFPLL3 */
	rf.RFPLL3.NUM2 = 0;
	/* RFPLL4 */
	rf.RFPLL4.KV_DEF = 8;
	rf.RFPLL4.CT_DEF = 7;
	rf.RFPLL4.DN = 0x145;
	/* CAL1 */
	rf.CAL1.LD_WINDOW = 2;
	rf.CAL1.M_CT_VALUE = 8;
	rf.CAL1.TLOCK = 7;
	rf.CAL1.TVCO = 0x0F;
	/* TXRX1 */
	rf.TXRX1.TXBYPASS = 0;
	rf.TXRX1.INTBIASEN = 0;
	rf.TXRX1.TXENMODE = 0;
	rf.TXRX1.TXDIFFMODE = 0;
	rf.TXRX1.TXLPFBW = 2;
	rf.TXRX1.RXLPFBW = 2;
	rf.TXRX1.TXVGC = 0;
	rf.TXRX1.PCONTROL = 0;
	rf.TXRX1.RXDCFBBYPS = 0;
	/* PCNT1 */
	rf.PCNT1.TX_DELAY = 0;
	rf.PCNT1.PC_OFFSET = 0;
	rf.PCNT1.P_DESIRED = 0;
	rf.PCNT1.MID_BIAS = 0;
	/* PCNT2 */
	rf.PCNT2.MIN_POWER = 0;
	rf.PCNT2.MID_POWER = 0;
	rf.PCNT2.MAX_POWER = 0;
	/* VCOT1 */
	rf.VCOT1.AUX1 = 0;
	rf.VCOT1.AUX = 0;
}

/*******************************************************************************

	wifimac IO: a lot of the wifi regs are action registers, that are mirrored
				without action, so the default IO via MMU.c does not seem to
				be very suitable

				all registers are 16 bit

 *******************************************************************************/

static void WIFI_TXStart(const WifiTXLocIndex txSlotIndex, IOREG_W_TXBUF_LOCATION &txLocation);

static void WIFI_triggerIRQMask(u16 mask)
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	
	u16 oldFlags = io.IF.value & io.IE.value;
	
	io.IF.value |= (mask & 0xFBFF);
	u16 newFlags = io.IF.value & io.IE.value;
	
	if (!oldFlags && newFlags)
	{
		NDS_makeIrq(ARMCPU_ARM7, IRQ_BIT_ARM7_WIFI); // cascade it via arm7 wifi irq
	}
}

static void WIFI_triggerIRQ(u8 irq)
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	
	switch (irq)
	{
		case WIFI_IRQ_TXSTART: // IRQ 7
			io.TX_SEQNO.Number++;
			break;
			
		case WIFI_IRQ_TIMEPREBEACON: // IRQ 15
		{
			if (io.POWER_TX.AutoWakeup != 0)
			{
				io.RF_STATUS.RFStatus = 0x1;
				io.RF_PINS.CarrierSense = 0;
				io.RF_PINS.TXMain = 0;
				io.RF_PINS.UNKNOWN1 = 1;
				io.RF_PINS.TX_On = 0;
				io.RF_PINS.RX_On = 1;
			}
			break;
		}
			
		case WIFI_IRQ_TIMEBEACON: // IRQ 14
		{
			io.BEACONCOUNT1 = io.BEACONINT.Interval;
			
			if (io.US_COMPARECNT.EnableCompare != 0)
			{
				io.BEACONCOUNT2 = 0xFFFF;
				io.TXREQ_READ.Loc1 = 0;
				io.TXREQ_READ.Loc2 = 0;
				io.TXREQ_READ.Loc3 = 0;
				
				WIFI_TXStart(WifiTXLocIndex_BEACON, io.TXBUF_BEACON);
				
				if (io.TXBUF_BEACON.TransferRequest != 0)
				{
					io.TXBUSY.Beacon = 1;
				}
				
				if (io.LISTENCOUNT.Count == 0)
				{
					io.LISTENCOUNT.Count = io.LISTENINT.Interval;
				}
				
				io.LISTENCOUNT.Count--;
			}
			break;
		}
			
		case WIFI_IRQ_TIMEPOSTBEACON: // IRQ 13
		{
			if (io.POWER_TX.AutoSleep != 0)
			{
				io.RF_STATUS.RFStatus = 0x9;
				io.RF_PINS.CarrierSense = 0;
				io.RF_PINS.TXMain = 1;
				io.RF_PINS.UNKNOWN1 = 1;
				io.RF_PINS.TX_On = 1;
				io.RF_PINS.RX_On = 0;
				
				io.INTERNAL_034 = 0x0002;
				io.TXREQ_READ.value &= 0x0010;
				io.POWERSTATE.WillPowerOn = 0;
				io.POWERSTATE.IsPowerOff = 1;
			}
			break;
		}
			
		case WIFI_IRQ_UNK: // IRQ 12
			WIFI_LOG(2, "IRQ 12 triggered.\n");
			io.TX_SEQNO.Number++;
			break;
	}

	WIFI_triggerIRQMask(1<<irq);
}

INLINE u16 WIFI_GetRXFlags(u8* packet)
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	u16 ret = 0x0010;
	u16 frameCtl = *(u16*)&packet[0];

	switch(frameCtl & 0x000C)
	{
	case 0x0000:  // Management frame
		if ((frameCtl & 0x00F0) == 0x0080)
			ret |= 0x0001;
		break;

	case 0x0004:  // Control frame
		ret |= 0x0005;
		break;

	case 0x0008:  // Data frame
		if (frameCtl == 0x0228)
			ret |= 0x000C;
		else
			ret |= 0x0008;
		break;
	}

	if (frameCtl & 0x0400)
		ret |= 0x0100;

	if (!memcmp(&packet[10], io.BSSID, 6))
		ret |= 0x8000;

	//printf("----- Computing RX flags for received frame: FrameCtl = %04X, Flags = %04X -----\n", frameCtl, ret);

	return ret;
}

INLINE void WIFI_MakeRXHeader(u8* buf, u16 flags, u16 xferRate, u16 len, u8 maxRSSI, u8 minRSSI)
{
	*(u16*)&buf[0] = flags;

	// Unknown (usually 0x0040)
	buf[2] = 0x40;
	buf[3] = 0x00;

	// Time since last packet??? Random??? Left unchanged???
	buf[4] = 0x01;
	buf[5] = 0x00;

	*(u16*)&buf[6] = xferRate;

	*(u16*)&buf[8] = len;

	buf[10] = 253;//maxRSSI;
	buf[11] = 255;//minRSSI;
}

static void WIFI_RXPutWord(u16 val)
{
	WifiData &wifi = wifiHandler->GetWifiData();
	WIFI_IOREG_MAP &io = wifi.io;
	
	/* abort when RX data queuing is not enabled */
	if (io.RXCNT.EnableRXFIFOQueuing == 0) return;
	/* abort when ringbuffer is full */
	//if (wifiMac.RXReadCursor == wifiMac.RXWriteCursor) return;
	/*if(wifiMac.RXWriteCursor >= wifiMac.RXReadCursor) 
	{
		printf("WIFI: write cursor (%04X) above READCSR (%04X). Cannot write received packet.\n", 
			wifiMac.RXWriteCursor, wifiMac.RXReadCursor);
		return;
	}*/
	/* write the data to cursor position */
	wifi.RAM[io.RXBUF_WRCSR.HalfwordAddress] = val;
//	printf("wifi: written word %04X to circbuf addr %04X\n", val, (wifiMac.RXWriteCursor << 1));
	/* move cursor by one */
	//printf("written one word to %04X (start %04X, end %04X), ", wifiMac.RXWriteCursor, wifiMac.RXRangeBegin, wifiMac.RXRangeEnd);
	io.RXBUF_WRCSR.HalfwordAddress++;
	/* wrap around */
//	wifiMac.RXWriteCursor %= (wifiMac.RXRangeEnd - wifiMac.RXRangeBegin) >> 1;
//	printf("new addr=%04X\n", wifiMac.RXWriteCursor);
	if (io.RXBUF_WRCSR.HalfwordAddress >= ((io.RXBUF_END & 0x1FFE) >> 1))
		io.RXBUF_WRCSR.HalfwordAddress = ((io.RXBUF_BEGIN & 0x1FFE) >> 1);
}

static void WIFI_TXStart(const WifiTXLocIndex txSlotIndex, IOREG_W_TXBUF_LOCATION &txLocation)
{
	WifiData &wifi = wifiHandler->GetWifiData();
	WIFI_IOREG_MAP &io = wifi.io;
	
	WIFI_LOG(3, "TX slot %i trying to send a packet: TXCnt = %04X, TXBufLoc = %04X\n", 
		(int)txSlotIndex, io.TXREQ_READ.value, txLocation.value);

	if (txLocation.TransferRequest != 0)	/* is slot enabled? */
	{
		//printf("send packet at %08X, lr=%08X\n", NDS_ARM7.instruct_adr, NDS_ARM7.R[14]);
		u16 txLen;
		u16 address = txLocation.HalfwordAddress;
		
		// is there even enough space for the header (6 hwords) in the tx buffer?
		if (address > 0x1000-6)
		{
			WIFI_LOG(1, "TX slot %i trying to send a packet overflowing from the TX buffer (address %04X). Attempt ignored.\n", 
				(int)txSlotIndex, (address << 1));
			return;
		}

		//printf("---------- SENDING A PACKET ON SLOT %i, FrameCtl = %04X ----------\n",
		//	slot, wifiMac.RAM[address+6]);

		// 12 byte header TX Header: http://www.akkit.org/info/dswifi.htm#FmtTx
		txLen = wifi.RAM[address+5];
		// zero length
		if (txLen == 0)
		{
			WIFI_LOG(1, "TX slot %i trying to send a packet with length field set to zero. Attempt ignored.\n", 
				(int)txSlotIndex);
			return;
		}

		// Align packet length
		txLen = WIFI_alignedLen(txLen);

		// unsupported txRate
		switch (wifi.RAM[address+4] & 0xFF)
		{
			case 10: // 1 mbit
			case 20: // 2 mbit
				break;
			default: // other rates
				WIFI_LOG(1, "TX slot %i trying to send a packet with transfer rate field set to an invalid value of %i. Attempt ignored.\n", 
					(int)txSlotIndex, wifi.RAM[address+4] & 0xFF);
				return;
		}

		// Set sequence number if required
		if ( (txSlotIndex == WifiTXLocIndex_BEACON) || (txLocation.IEEESeqCtrl == 0) )
		{
		//	u16 seqctl = wifiMac.RAM[address + 6 + 22];
		//	wifiMac.RAM[address + 6 + 11] = (seqctl & 0x000F) | (wifiMac.TXSeqNo << 4);
			wifi.RAM[address + 6 + 11] = io.TX_SEQNO.Number << 4;
		}

		// Calculate and set FCS
		u32 crc32 = WIFI_calcCRC32((u8*)&wifi.RAM[address + 6], txLen - 4);
		*(u32*)&wifi.RAM[address + 6 + ((txLen-4) >> 1)] = crc32;

		WIFI_triggerIRQ(WIFI_IRQ_TXSTART);
		
		if ( (txSlotIndex == WifiTXLocIndex_LOC1) || (txSlotIndex == WifiTXLocIndex_LOC2) || (txSlotIndex == WifiTXLocIndex_LOC3) )
		{
			TXPacketInfo &txPacketInfo = wifiHandler->GetPacketInfoAtSlot(txSlotIndex);
			txPacketInfo.bodyLen = txLen;
			txPacketInfo.remainingBytes = txPacketInfo.bodyLen + 12;
			
			switch (txSlotIndex)
			{
				case WifiTXLocIndex_LOC1: io.TXBUSY.Loc1 = 1; break;
				//case WifiTXLocIndex_CMD: io.TXBUSY.Cmd = 1; break;
				case WifiTXLocIndex_LOC2: io.TXBUSY.Loc2 = 1; break;
				case WifiTXLocIndex_LOC3: io.TXBUSY.Loc3 = 1; break;
				//case WifiTXLocIndex_BEACON: io.TXBUSY.Beacon = 1; break;
					
				default:
					break;
			}
			
			if (txSlotIndex == WifiTXLocIndex_LOC3)
			{
				wifi.txCurrentSlot = WifiTXLocIndex_LOC3;
			}
			else if ( (txSlotIndex == WifiTXLocIndex_LOC2) && (wifi.txCurrentSlot == WifiTXLocIndex_LOC1) )
			{
				wifi.txCurrentSlot = WifiTXLocIndex_LOC2;
			}
			
			io.RXTX_ADDR.HalfwordAddress = address;
			
			io.RF_STATUS.RFStatus = 0x03;
			io.RF_PINS.CarrierSense = 0;
			io.RF_PINS.TXMain = 1;
			io.RF_PINS.UNKNOWN1 = 1;
			io.RF_PINS.TX_On = 1;
			io.RF_PINS.RX_On = 0;
#if 0
			WIFI_SoftAP_RecvPacketFromDS((u8*)&wifi.RAM[address+6], txLen);
			WIFI_triggerIRQ(WIFI_IRQ_TXEND);
			
			wifi.RAM[address] = 0x0001;
			wifi.RAM[address+4] &= 0x00FF;
#endif
		}
		else if (txSlotIndex == WifiTXLocIndex_CMD)
		{
			wifiHandler->CommSendPacket((u8 *)&wifi.RAM[address+6], txLen);
			WIFI_triggerIRQ(WIFI_IRQ_UNK);
			
			// If bit 13 is set, then it has priority over bit 14
			if (io.TXSTATCNT.UpdateTXStat_0B01 != 0)
			{
				WIFI_triggerIRQ(WIFI_IRQ_TXEND);
				io.TXSTAT.value = 0x0B01;
			}
			else if (io.TXSTATCNT.UpdateTXStat_0800 != 0)
			{
				WIFI_triggerIRQ(WIFI_IRQ_TXEND);
				io.TXSTAT.value = 0x0800;
			}
			
			txLocation.TransferRequest = 0;
			
			wifi.RAM[address] = 0x0001;
			wifi.RAM[address+4] &= 0x00FF;
		}
		else if (txSlotIndex == WifiTXLocIndex_BEACON)
		{
			// Set timestamp
			*(u64*)&wifi.RAM[address + 6 + 12] = io.US_COUNT;
			
			wifiHandler->CommSendPacket((u8 *)&wifi.RAM[address+6], txLen);
			
			if (io.TXSTATCNT.UpdateTXStatBeacon != 0)
			{
				WIFI_triggerIRQ(WIFI_IRQ_TXEND);
				io.TXSTAT.value = 0x0301;
			}
			
			wifi.RAM[address] = 0x0001;
			wifi.RAM[address+4] &= 0x00FF;
		}
	}
}

void WIFI_write16(u32 address, u16 val)
{
	bool action = false;
	if (!nds.power2.wifi) return;
	
	WifiData &wifi = wifiHandler->GetWifiData();
	WIFI_IOREG_MAP &io = wifi.io;

	u32 page = address & 0x7000;

	// 0x2000 - 0x3FFF: unused
	if ((page >= 0x2000) && (page < 0x4000))
		return;

	WIFI_LOG(5, "Write at address %08X, %04X\n", address, val);
	//printf("WIFI: Write at address %08X, %04X, pc=%08X\n", address, val, NDS_ARM7.instruct_adr);

	// 0x4000 - 0x5FFF: wifi RAM
	if ((page >= 0x4000) && (page < 0x6000))
	{
		/* access to the circular buffer */
		address &= 0x1FFF;
        wifi.RAM[address >> 1] = val;
		return;
	}

	// anything else: I/O ports
	// only the first mirror (0x0000 - 0x0FFF) causes a special action
	if (page == 0x0000) action = true;

	address &= 0x0FFF;
	switch (address)
	{
		case REG_WIFI_MODE: // 0x004
		{
			IOREG_W_MODE_RST MODE_RST;
			MODE_RST.value = val;
			
			io.MODE_RST.UNKNOWN1 = MODE_RST.UNKNOWN1;
			io.MODE_RST.UNKNOWN2 = MODE_RST.UNKNOWN2;
			io.MODE_RST.UNKNOWN3 = MODE_RST.UNKNOWN3;
			
			if ( (io.MODE_RST.TXMasterEnable == 0) && (MODE_RST.TXMasterEnable != 0) )
			{
				io.INTERNAL_034						= 0x0002;
				io.RF_STATUS.value					= 0x0009;
				io.RF_PINS.value					= 0x0046;
				io.INTERNAL_27C						= 0x0005;
				
				// According to GBATEK, the following registers might be reset to some unknown values.
				// io.INTERNAL_2A2					= ???;
			}
			
			if ( (io.MODE_RST.TXMasterEnable != 0) && (MODE_RST.TXMasterEnable == 0) )
			{
				io.INTERNAL_27C			 			= 0x000A;
			}
			
			io.MODE_RST.TXMasterEnable = MODE_RST.TXMasterEnable;
			
			if (MODE_RST.ResetPortSet1 != 0)
			{
				io.RXBUF_WR_ADDR.value				= 0x0000;
				io.CMD_TOTALTIME		 			= 0x0000;
				io.CMD_REPLYTIME		 			= 0x0000;
				io.X_1A4							= 0x0000;
				io.INTERNAL_278			 			= 0x000F;
				
				// According to GBATEK, the following registers might be reset to some unknown values.
				//io.TXREQ_SET.value				= ???;
				//io.INTERNAL_0BA					= ???;
				//io.INTERNAL_204					= ???;
				//io.INTERNAL_25C					= ???;
				//io.RXTX_ADDR.value				= ???;
				//io.INTERNAL_274					= ???;
			}
			
			if (MODE_RST.ResetPortSet2 != 0)
			{
				io.MODE_WEP.value 					= 0x0000;
				io.TXSTATCNT.value					= 0x0000;
				io.X_00A							= 0x0000;
				io.MACADDR[0]						= 0x00;
				io.MACADDR[1]						= 0x00;
				io.MACADDR[2]						= 0x00;
				io.MACADDR[3]						= 0x00;
				io.MACADDR[4]						= 0x00;
				io.MACADDR[5]						= 0x00;
				io.BSSID[0]							= 0x00;
				io.BSSID[1]							= 0x00;
				io.BSSID[2]							= 0x00;
				io.BSSID[3]							= 0x00;
				io.BSSID[4]							= 0x00;
				io.BSSID[5]							= 0x00;
				io.AID_LOW.value					= 0x0000;
				io.AID_FULL.value					= 0x0000;
				io.TX_RETRYLIMIT.value				= 0x0707;
				io.INTERNAL_02E			 			= 0x0000;
				io.RXBUF_BEGIN						= 0x4000;
				io.RXBUF_END						= 0x4800;
				io.TXBUF_TIM.value					= 0x0000;
				io.PREAMBLE.value 					= 0x0001;
				io.RXFILTER.value			 		= 0x0401;
				io.CONFIG_0D4						= 0x0001;
				io.RXFILTER2.value					= 0x0008;
				io.CONFIG_0EC			 			= 0x3F03;
				io.TX_HDR_CNT.value		 			= 0x0000;
				io.INTERNAL_198 					= 0x0000;
				io.X_1A2.value			 			= 0x0001;
				io.INTERNAL_224			 			= 0x0003;
				io.INTERNAL_230			 			= 0x0047;
			}
			break;
		}
			
		case REG_WIFI_WEP: // 0x006
			io.MODE_WEP.value = val;
			break;
			
		case REG_WIFI_TXSTATCNT: // 0x008
			io.TXSTATCNT.value = val;
			break;
			
		case REG_WIFI_X_00A: // 0x00A
			io.X_00A = val;
			break;
			
		case REG_WIFI_IF: // 0x010
			io.IF.value &= ~val;
			break;
			
		case REG_WIFI_IE: // 0x012
			io.IE.value = val;
			break;
			
		case REG_WIFI_MAC0: // 0x018
			io.MACADDR0.value = val;
			break;
			
		case REG_WIFI_MAC1: // 0x01A
			io.MACADDR1.value = val;
			break;
			
		case REG_WIFI_MAC2: // 0x01C
			io.MACADDR2.value = val;
			break;
			
		case REG_WIFI_BSS0: // 0x020
			io.BSSID0.value = val;
			break;
			
		case REG_WIFI_BSS1: // 0x022
			io.BSSID1.value = val;
			break;
			
		case REG_WIFI_BSS2: // 0x024
			io.BSSID2.value = val;
			break;
			
		case REG_WIFI_AID_LOW: // 0x028
			io.AID_LOW.value = val & 0x000F;
			break;
			
		case REG_WIFI_AID_HIGH: // 0x02A
			io.AID_FULL.value = val & 0x07FF;
			break;
			
		case REG_WIFI_RETRYLIMIT: // 0x02C
			io.TX_RETRYLIMIT.value = val;
			break;
			
		case 0x02E: // 0x02E
			io.INTERNAL_02E = val;
			break;
			
		case REG_WIFI_RXCNT: // 0x030
		{
			IOREG_W_RXCNT RXCNT;
			RXCNT.value = val & 0xFF8F;
			
			io.RXCNT.EnableRXFIFOQueuing = RXCNT.EnableRXFIFOQueuing;
			
			if (io.RXCNT.UNKNOWN1 != RXCNT.UNKNOWN1)
			{
				io.RXCNT.UNKNOWN1 = RXCNT.UNKNOWN1;
				WIFI_LOG(2, "Writing value of %04Xh to RXCNT.UNKNOWN1\n", (int)io.RXCNT.UNKNOWN1);
			}
			
			if (io.RXCNT.UNKNOWN2 != RXCNT.UNKNOWN2)
			{
				io.RXCNT.UNKNOWN2 = RXCNT.UNKNOWN2;
				WIFI_LOG(2, "Writing value of %04Xh to RXCNT.UNKNOWN2\n", (int)io.RXCNT.UNKNOWN2);
			}
			
			if (RXCNT.CopyAddrToWRCSR != 0)
			{
				io.RXBUF_WRCSR.HalfwordAddress = io.RXBUF_WR_ADDR.HalfwordAddress;
			}
			
			if (RXCNT.CopyTXBufReply1To2 != 0)
			{
				io.TXBUF_REPLY2.HalfwordAddress = io.TXBUF_REPLY1.HalfwordAddress;
				io.TXBUF_REPLY1.value = 0x0000;
			}
			break;
		}
			
		case REG_WIFI_WEPCNT: // 0x032
			io.WEP_CNT.value = val & 0x8000;
			break;
			
		case 0x034: // 0x034
			io.INTERNAL_034 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_034\n", (int)io.INTERNAL_034);
			break;
			
		case REG_WIFI_POWER_US: // 0x036
		{
			IOREG_W_POWER_US POWER_US;
			POWER_US.value = val & 0x0003;
			
			io.POWER_US.Disable = POWER_US.Disable;
			
			if (io.POWER_US.UNKNOWN1 != POWER_US.UNKNOWN1)
			{
				io.POWER_US.UNKNOWN1 = POWER_US.UNKNOWN1;
				WIFI_LOG(2, "Writing value of %d to POWER_US.UNKNOWN1\n", (int)POWER_US.UNKNOWN1);
			}
			break;
		}
			
		case REG_WIFI_POWER_TX: // 0x038
		{
			IOREG_W_POWER_TX POWER_TX;
			POWER_TX.value = val & 0x000F;
			
			io.POWER_TX.AutoWakeup = POWER_TX.AutoWakeup;
			io.POWER_TX.AutoSleep = POWER_TX.AutoSleep;
			
			if (io.POWER_TX.UNKNOWN1 != POWER_TX.UNKNOWN1)
			{
				io.POWER_TX.UNKNOWN1 = POWER_TX.UNKNOWN1;
				WIFI_LOG(2, "Writing value of %d to POWER_TX.UNKNOWN1\n", (int)POWER_TX.UNKNOWN1);
			}
			
			if (POWER_TX.UNKNOWN2 != 0)
			{
				WIFI_LOG(2, "Performing unknown action of POWER_TX.UNKNOWN2\n");
			}
			break;
		}
			
		case REG_WIFI_POWERSTATE: // 0x03C
		{
			IOREG_W_POWERSTATE newPowerState;
			newPowerState.value = val;
			
			io.POWERSTATE.UNKNOWN1 = newPowerState.UNKNOWN1;
			io.POWERSTATE.RequestPowerOn = newPowerState.RequestPowerOn;
			
			if (io.POWERSTATE.RequestPowerOn != 0)
			{
				WIFI_triggerIRQ(WIFI_IRQ_RFWAKEUP);
				
				// This is supposedly the TX beacon transfer flag.
				// Since reading TXREQ_READ bit 4 should always return a 1 (unless
				// POWERFORCE clears it), we'll simply set this bit to 1 on power up
				// so that bit 4 gets its default value.
				io.TXREQ_READ.UNKNOWN1 = 1;
				
				// Most likely, there should be some delay between firing the IRQ and
				// setting the register bits. But we're not going to emulate the delay
				// right now, so we simply change all of the bits immediately.
				io.POWERSTATE.RequestPowerOn = 0;
				io.POWERSTATE.WillPowerOn = 0;
				io.POWERSTATE.IsPowerOff = 0;
			}
			break;
		}
			
		case REG_WIFI_POWERFORCE: // 0x040
		{
			
			io.POWERFORCE.value = val & 0x8001;
			
			if (io.POWERFORCE.ApplyNewPowerOffState != 0)
			{
				if ( (io.POWERFORCE.NewPowerOffState != 0) && (io.POWERSTATE.IsPowerOff == 0) )
				{
					// Immediate action
					io.INTERNAL_034				= 0x0002;
					io.TXREQ_READ.value			= 0x0000; // Note that even bit 4 gets cleared here
					io.RF_STATUS.value			= 0x0009;
					io.RF_PINS.value			= 0x0046;
					
					io.POWERSTATE.WillPowerOn	= 0;
					io.POWERSTATE.IsPowerOff	= 1;
				}
				else if ( (io.POWERFORCE.NewPowerOffState == 0) && (io.POWERSTATE.IsPowerOff != 0) )
				{
					// Delayed action
					io.POWERSTATE.WillPowerOn	= 1;
				}
				
				// This probably shouldn't happen right here, but we need to write to
				// POWERSTATE.IsPowerOff because we need to keep the power on in order
				// to force certain in-game errors to occur. Otherwise, the user might
				// get caught up in an infinite loop.
				if (io.POWERSTATE.WillPowerOn != 0)
				{
					io.POWERSTATE.IsPowerOff	= io.POWERFORCE.NewPowerOffState;
				}
			}
			break;
		}
			
		case REG_WIFI_POWER_UNK: // 0x048
			io.POWER_UNKNOWN.value = val;
			break;
			
		case REG_WIFI_RXRANGEBEGIN: // 0x050
		{
			io.RXBUF_BEGIN = val;
			if (io.RXBUF_WRCSR.HalfwordAddress < ((io.RXBUF_BEGIN & 0x1FFE) >> 1))
			{
				io.RXBUF_WRCSR.HalfwordAddress = ((io.RXBUF_BEGIN & 0x1FFE) >> 1);
			}
			break;
		}
			
		case REG_WIFI_RXRANGEEND: // 0x052
		{
			io.RXBUF_END = val;
			if (io.RXBUF_WRCSR.HalfwordAddress >= ((io.RXBUF_END & 0x1FFE) >> 1))
			{
				io.RXBUF_WRCSR.HalfwordAddress = ((io.RXBUF_BEGIN & 0x1FFE) >> 1);
			}
			break;
		}
			
		case REG_WIFI_WRITECSRLATCH: // 0x056
			io.RXBUF_WR_ADDR.value = val & 0x0FFF;
			break;
			
		case REG_WIFI_CIRCBUFRADR: // 0x058
			io.RXBUF_RD_ADDR.value = val & 0x1FFE;
			break;
			
		case REG_WIFI_RXREADCSR: // 0x05A
			io.RXBUF_READCSR.value = val & 0x0FFF;
			break;
			
		case REG_WIFI_RXBUF_COUNT: // 0x05C
			io.RXBUF_COUNT.value = val & 0x0FFF;
			break;
			
		case REG_WIFI_CIRCBUFRD_END: // 0x062
			io.RXBUF_GAP.value = val & 0x1FFE;
			break;
			
		case REG_WIFI_CIRCBUFRD_SKIP: // 0x064
			io.RXBUF_GAPDISP.value = val & 0x0FFF;
			break;
			
		case REG_WIFI_CIRCBUFWADR: // 0x068
			io.TXBUF_WR_ADDR.value = val & 0x1FFE;
			break;
			
		case REG_WIFI_TXBUFCOUNT: // 0x06C
			io.TXBUF_COUNT.value = val & 0x0FFF;
			break;
			
		case REG_WIFI_CIRCBUFWRITE: // 0x070
		{
			/* set value into the circ buffer, and move cursor to the next hword on action */
			//printf("wifi: circbuf fifo write at %04X, %04X (action=%i)\n", (wifiMac.CircBufWriteAddress & 0x1FFF), val, action);
			wifi.RAM[io.TXBUF_WR_ADDR.HalfwordAddress] = val;
			if (action)
			{
				/* move to next hword */
                io.TXBUF_WR_ADDR.HalfwordAddress++;
				if (io.TXBUF_WR_ADDR.HalfwordAddress == io.TXBUF_GAP.HalfwordAddress)
				{
					/* on end of buffer, add skip hwords to it */
					io.TXBUF_GAP.HalfwordAddress += io.TXBUF_GAPDISP.HalfwordOffset;
				}
			}
			break;
		}
			
		case REG_WIFI_CIRCBUFWR_END: // 0x074
			io.TXBUF_GAP.value = val & 0x1FFF;
			break;
			
		case REG_WIFI_CIRCBUFWR_SKIP: // 0x076
			io.TXBUF_GAPDISP.value = val & 0x0FFF;
			break;
			
		case 0x078: // 0x078
			io.INTERNAL_078 = val;
			break;
			
		case REG_WIFI_TXLOCBEACON: // 0x080
		{
			IOREG_W_TXBUF_LOCATION TXBUF_BEACON;
			TXBUF_BEACON.value = val;
			
			io.TXBUF_BEACON.HalfwordAddress = TXBUF_BEACON.HalfwordAddress;
			io.TXBUF_BEACON.Bit12 = TXBUF_BEACON.Bit12;
			io.TXBUF_BEACON.IEEESeqCtrl = TXBUF_BEACON.IEEESeqCtrl;
			io.TXBUF_BEACON.TransferRequest = TXBUF_BEACON.TransferRequest;
			
			if (io.TXBUF_BEACON.UNKNOWN1 != TXBUF_BEACON.UNKNOWN1)
			{
				io.TXBUF_BEACON.UNKNOWN1 = TXBUF_BEACON.UNKNOWN1;
				WIFI_LOG(2, "Writing value of %d to TXBUF_BEACON.UNKNOWN1\n", (int)TXBUF_BEACON.UNKNOWN1);
			}
			
			if (io.TXBUF_BEACON.TransferRequest != 0)
			{
				WIFI_LOG(3, "Beacon transmission enabled to send the packet at %08X every %i milliseconds.\n",
						 0x04804000 + (io.TXBUF_BEACON.HalfwordAddress << 1), io.BEACONINT.Interval);
			}
			break;
		}
			
		case REG_WIFI_TXBUF_TIM: // 0x084
			io.TXBUF_TIM.value = val & 0x00FF;
			break;
			
		case REG_WIFI_LISTENCOUNT: // 0x088
			io.LISTENCOUNT.value = val & 0x00FF;
			break;
			
		case REG_WIFI_BEACONPERIOD: // 0x08C
			io.BEACONINT.value = val & 0x03FF;
			break;
			
		case REG_WIFI_LISTENINT: // 0x08E
			io.LISTENINT.value = val & 0x00FF;
			break;
			
		case REG_WIFI_TXLOCEXTRA: // 0x090
		{
			IOREG_W_TXBUF_LOCATION TXBUF_CMD;
			TXBUF_CMD.value = val;
			
			io.TXBUF_CMD.HalfwordAddress = TXBUF_CMD.HalfwordAddress;
			io.TXBUF_CMD.Bit12 = TXBUF_CMD.Bit12;
			io.TXBUF_CMD.IEEESeqCtrl = TXBUF_CMD.IEEESeqCtrl;
			
			if (wifi.cmdCount_u32 != 0)
			{
				io.TXBUF_CMD.TransferRequest = TXBUF_CMD.TransferRequest;
			}
			
			if (io.TXBUF_CMD.UNKNOWN1 != TXBUF_CMD.UNKNOWN1)
			{
				io.TXBUF_CMD.UNKNOWN1 = TXBUF_CMD.UNKNOWN1;
				WIFI_LOG(2, "Writing value of %d to TXBUF_CMD.UNKNOWN1\n", (int)TXBUF_CMD.UNKNOWN1);
			}
			break;
		}
			
		case REG_WIFI_TXBUF_REPLY1: // 0x094
			io.TXBUF_REPLY1.value = val;
			break;
			
		case 0x09C: // 0x09C
			io.INTERNAL_09C = val;
			break;
			
		case REG_WIFI_TXLOC1: // 0x0A0
		{
			IOREG_W_TXBUF_LOCATION TXBUF_LOCn;
			TXBUF_LOCn.value = val;
			
			io.TXBUF_LOC1.HalfwordAddress = TXBUF_LOCn.HalfwordAddress;
			io.TXBUF_LOC1.Bit12 = TXBUF_LOCn.Bit12;
			io.TXBUF_LOC1.IEEESeqCtrl = TXBUF_LOCn.IEEESeqCtrl;
			io.TXBUF_LOC1.TransferRequest = TXBUF_LOCn.TransferRequest;
			
			if (io.TXBUF_LOC1.UNKNOWN1 != TXBUF_LOCn.UNKNOWN1)
			{
				io.TXBUF_LOC1.UNKNOWN1 = TXBUF_LOCn.UNKNOWN1;
				WIFI_LOG(2, "Writing value of %d to TXBUF_LOC1.UNKNOWN1\n", (int)TXBUF_LOCn.UNKNOWN1);
			}
			break;
		}
			
		case REG_WIFI_TXLOC2: // 0x0A4
		{
			IOREG_W_TXBUF_LOCATION TXBUF_LOCn;
			TXBUF_LOCn.value = val;
			
			io.TXBUF_LOC2.HalfwordAddress = TXBUF_LOCn.HalfwordAddress;
			io.TXBUF_LOC2.Bit12 = TXBUF_LOCn.Bit12;
			io.TXBUF_LOC2.IEEESeqCtrl = TXBUF_LOCn.IEEESeqCtrl;
			io.TXBUF_LOC2.TransferRequest = TXBUF_LOCn.TransferRequest;
			
			if (io.TXBUF_LOC2.UNKNOWN1 != TXBUF_LOCn.UNKNOWN1)
			{
				io.TXBUF_LOC2.UNKNOWN1 = TXBUF_LOCn.UNKNOWN1;
				WIFI_LOG(2, "Writing value of %d to TXBUF_LOC2.UNKNOWN1\n", (int)TXBUF_LOCn.UNKNOWN1);
			}
			break;
		}
			
		case REG_WIFI_TXLOC3: // 0x0A8
		{
			IOREG_W_TXBUF_LOCATION TXBUF_LOCn;
			TXBUF_LOCn.value = val;
			
			io.TXBUF_LOC3.HalfwordAddress = TXBUF_LOCn.HalfwordAddress;
			io.TXBUF_LOC3.Bit12 = TXBUF_LOCn.Bit12;
			io.TXBUF_LOC3.IEEESeqCtrl = TXBUF_LOCn.IEEESeqCtrl;
			io.TXBUF_LOC3.TransferRequest = TXBUF_LOCn.TransferRequest;
			
			if (io.TXBUF_LOC3.UNKNOWN1 != TXBUF_LOCn.UNKNOWN1)
			{
				io.TXBUF_LOC3.UNKNOWN1 = TXBUF_LOCn.UNKNOWN1;
				WIFI_LOG(2, "Writing value of %d to TXBUF_LOC3.UNKNOWN1\n", (int)TXBUF_LOCn.UNKNOWN1);
			}
			break;
		}
			
		case REG_WIFI_TXREQ_RESET: // 0x0AC
		{
			IOREG_W_TXREQ_RESET TXREQ_RESET;
			TXREQ_RESET.value = val;
			
			if (TXREQ_RESET.Loc1 != 0)
			{
				io.TXREQ_READ.Loc1 = 0;
			}
			
			if (TXREQ_RESET.Cmd != 0)
			{
				io.TXREQ_READ.Cmd = 0;
			}
			
			if (TXREQ_RESET.Loc2 != 0)
			{
				io.TXREQ_READ.Loc2 = 0;
			}
			
			if (TXREQ_RESET.Loc3 != 0)
			{
				io.TXREQ_READ.Loc3 = 0;
			}
			
			if (TXREQ_RESET.UNKNOWN1 != 0)
			{
				WIFI_LOG(2, "Prevented clearing of TXREQ_READ.UNKNOWN1, for beacon?\n");
			}
			break;
		}
			
		case REG_WIFI_TXREQ_SET: // 0x0AE
		{
			IOREG_W_TXREQ_SET TXREQ_SET;
			TXREQ_SET.value = val;
			
			if (TXREQ_SET.Loc1 != 0)
			{
				io.TXREQ_READ.Loc1 = 1;
				WIFI_TXStart(WifiTXLocIndex_LOC1, io.TXBUF_LOC1);
			}
			
			if (TXREQ_SET.Cmd != 0)
			{
				io.TXREQ_READ.Cmd = 1;
				WIFI_TXStart(WifiTXLocIndex_CMD, io.TXBUF_CMD);
			}
			
			if (TXREQ_SET.Loc2 != 0)
			{
				io.TXREQ_READ.Loc2 = 1;
				WIFI_TXStart(WifiTXLocIndex_LOC2, io.TXBUF_LOC2);
			}
			
			if (TXREQ_SET.Loc3 != 0)
			{
				io.TXREQ_READ.Loc3 = 1;
				WIFI_TXStart(WifiTXLocIndex_LOC3, io.TXBUF_LOC3);
			}
			
			if (TXREQ_SET.UNKNOWN1 != 0)
			{
				io.TXREQ_READ.UNKNOWN1 = 1;
				WIFI_LOG(2, "Setting of TXREQ_SET.UNKNOWN1, for beacon?\n");
			}
			break;
		}
			
		case REG_WIFI_TXRESET: // 0x0B4
		{
			IOREG_W_TXBUF_RESET TXBUF_RESET;
			TXBUF_RESET.value = val;
			
			if (TXBUF_RESET.Loc1 != 0)
			{
				io.TXBUF_LOC1.TransferRequest = 0;
			}
			
			if (TXBUF_RESET.Cmd != 0)
			{
				io.TXBUF_CMD.TransferRequest = 0;
			}
			
			if (TXBUF_RESET.Loc2 != 0)
			{
				io.TXBUF_LOC2.TransferRequest = 0;
			}
			
			if (TXBUF_RESET.Loc3 != 0)
			{
				io.TXBUF_LOC3.TransferRequest = 0;
			}
			
			if (TXBUF_RESET.Reply2 != 0)
			{
				io.TXBUF_REPLY2.Enable = 0;
			}
			
			if (TXBUF_RESET.Reply1 != 0)
			{
				io.TXBUF_REPLY1.Enable = 0;
			}
			
			const bool reportUnknownWrite = (val != 0xFFFF);
			
			if (io.TXBUF_RESET.UNKNOWN1 != TXBUF_RESET.UNKNOWN1)
			{
				io.TXBUF_RESET.UNKNOWN1 = TXBUF_RESET.UNKNOWN1;
				
				if (reportUnknownWrite)
				{
					WIFI_LOG(2, "Writing value of %04Xh to TXBUF_RESET.UNKNOWN1\n", (int)io.TXBUF_RESET.UNKNOWN1);
				}
			}
			
			if (io.TXBUF_RESET.UNKNOWN2 != TXBUF_RESET.UNKNOWN2)
			{
				io.TXBUF_RESET.UNKNOWN2 = TXBUF_RESET.UNKNOWN2;
				
				if (reportUnknownWrite)
				{
					WIFI_LOG(2, "Writing value of %04Xh to TXBUF_RESET.UNKNOWN2\n", (int)io.TXBUF_RESET.UNKNOWN2);
				}
			}
			break;
		}
			
		case 0x0BA: // 0x0BA
			io.INTERNAL_0BA = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_0BA\n", (int)io.INTERNAL_0BA);
			break;
			
		case REG_WIFI_PREAMBLE: // 0x0BC
			io.PREAMBLE.value = val;
			break;
			
		case REG_WIFI_CMD_TOTALTIME: // 0x0C0
			io.CMD_TOTALTIME = val;
			break;
			
		case REG_WIFI_CMD_REPLYTIME: // 0x0C4
			io.CMD_REPLYTIME = val;
			break;
			
		case 0x0C8: // 0x0C8
			io.INTERNAL_0C8 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_0C8\n", (int)io.INTERNAL_0C8);
			break;
			
		case REG_WIFI_RXFILTER: // 0x0D0
			io.RXFILTER.value = val;
			break;
			
		case REG_WIFI_CONFIG_0D4: // 0x0D4
			io.CONFIG_0D4 = val;
			break;
			
		case REG_WIFI_CONFIG_0D8: // 0x0D8
			io.CONFIG_0D8 = val;
			break;
			
		case REG_WIFI_RX_LEN_CROP: // 0x0DA
			io.RX_LEN_CROP.value = val;
			break;
			
		case REG_WIFI_RXFILTER2: // 0x0E0
			io.RXFILTER2.value = val;
			break;
			
		case REG_WIFI_USCOUNTERCNT: // 0x0E8
			io.US_COUNTCNT.value = val & 0x0001;
			break;
			
		case REG_WIFI_USCOMPARECNT: // 0x0EA
		{
			IOREG_W_US_COMPARECNT US_COMPARECNT;
			US_COMPARECNT.value = val & 0x0003;
			
			io.US_COMPARECNT.EnableCompare = US_COMPARECNT.EnableCompare;
			
			if (US_COMPARECNT.ForceIRQ14 != 0)
			{
				WIFI_triggerIRQ(WIFI_IRQ_TIMEBEACON);
			}
			break;
		}
			
		case REG_WIFI_CONFIG_0EC: // 0x0EC
			io.CONFIG_0EC = val;
			break;
			
		case REG_WIFI_EXTRACOUNTCNT: // 0x0EE
			io.CMD_COUNTCNT.value = val & 0x0001;
			break;
			
		case REG_WIFI_USCOMPARE0: // 0x0F0
			io.US_COMPARE0 = val;
			break;
			
		case REG_WIFI_USCOMPARE1: // 0x0F2
			io.US_COMPARE1 = val;
			break;
			
		case REG_WIFI_USCOMPARE2: // 0x0F4
			io.US_COMPARE2 = val;
			break;
			
		case REG_WIFI_USCOMPARE3: // 0x0F6
			io.US_COMPARE3 = val;
			break;
			
		case REG_WIFI_USCOUNTER0: // 0x0F8
			io.US_COUNT0 = val;
			break;
			
		case REG_WIFI_USCOUNTER1: // 0x0FA
			io.US_COUNT1 = val;
			break;
			
		case REG_WIFI_USCOUNTER2: // 0x0FC
			io.US_COUNT2 = val;
			break;
			
		case REG_WIFI_USCOUNTER3: // 0x0FE
			io.US_COUNT3 = val;
			break;
			
		case 0x100: // 0x100
			io.INTERNAL_100 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_100\n", (int)io.INTERNAL_100);
			break;
			
		case 0x102: // 0x102
			io.INTERNAL_102 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_102\n", (int)io.INTERNAL_102);
			break;
			
		case 0x104: // 0x104
			io.INTERNAL_104 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_104\n", (int)io.INTERNAL_104);
			break;
			
		case 0x106: // 0x106
			io.INTERNAL_106 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_106\n", (int)io.INTERNAL_106);
			break;
			
		case REG_WIFI_CONTENTFREE: // 0x10C
			io.CONTENTFREE = val;
			break;
			
		case REG_WIFI_PREBEACONCOUNT: // 0x110
			io.PRE_BEACON = val;
			break;
			
		case REG_WIFI_EXTRACOUNT: // 0x118
			io.CMD_COUNT = val;
			wifi.cmdCount_u32 = (u32)val * 10;
			break;
			
		case REG_WIFI_BEACONCOUNT1: // 0x11C
			io.BEACONCOUNT1 = val;
			break;
			
		case REG_WIFI_CONFIG_120: // 0x120
			io.CONFIG_120 = val;
			break;
			
		case REG_WIFI_CONFIG_122: // 0x122
			io.CONFIG_122 = val;
			break;
			
		case REG_WIFI_CONFIG_124: // 0x124
			io.CONFIG_124 = val;
			break;
			
		case 0x126: // 0x126
			io.INTERNAL_126 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_126\n", (int)io.INTERNAL_126);
			break;
			
		case REG_WIFI_CONFIG_128: // 0x128
			io.CONFIG_128 = val;
			break;
			
		case 0x12A: // 0x12A
			io.INTERNAL_12A = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_12A\n", (int)io.INTERNAL_12A);
			break;
			
		case REG_WIFI_CONFIG_130: // 0x130
			io.CONFIG_130 = val;
			break;
			
		case REG_WIFI_CONFIG_132: // 0x132
			io.CONFIG_132 = val;
			break;
			
		case REG_WIFI_BEACONCOUNT2: // 0x134
			io.BEACONCOUNT2 = val;
			break;
			
		case REG_WIFI_CONFIG_140: // 0x140
			io.CONFIG_140 = val;
			break;
			
		case REG_WIFI_CONFIG_142: // 0x142
			io.CONFIG_142 = val;
			break;
			
		case REG_WIFI_CONFIG_144: // 0x144
			io.CONFIG_144 = val;
			break;
			
		case REG_WIFI_CONFIG_146: // 0x146
			io.CONFIG_146 = val;
			break;
			
		case REG_WIFI_CONFIG_148: // 0x148
			io.CONFIG_148 = val;
			break;
			
		case REG_WIFI_CONFIG_14A: // 0x14A
			io.CONFIG_14A = val;
			break;
			
		case REG_WIFI_CONFIG_14C: // 0x14C
			io.CONFIG_14C = val;
			break;
			
		case REG_WIFI_CONFIG_150: // 0x150
			io.CONFIG_150 = val;
			break;
			
		case REG_WIFI_CONFIG_154: // 0x154
			io.CONFIG_154 = val;
			break;
			
		case REG_WIFI_BBCNT: // 0x158
		{
			bb_t &bb = wifiHandler->GetWifiData().bb;
			
			io.BB_CNT.value = val & 0xF0FF;
			
			if (io.BB_CNT.Index > 0x68)
			{
				WIFI_LOG(2, "Writing value of %02Xh to BB_CNT.Index, should be 00h-68h\n", (int)io.BB_CNT.Index);
			}
			else
			{
				io.BB_BUSY.Busy = 1;
				
				if (io.BB_CNT.Direction == 5)  // Perform a write
				{
					bb.data[io.BB_CNT.Index] = io.BB_WRITE.Data;
				}
				else if (io.BB_CNT.Direction == 6) // Perform a read
				{
					io.BB_READ.Data = bb.data[io.BB_CNT.Index];
				}
				
				// Normally, there should be some sort of transfer delay, but we're
				// not going to emulate the delay right now. Therefore, assume for
				// now that the transfer occurs instantaneously and clear the busy
				// flag accordingly.
				io.BB_BUSY.Busy = 0;
			}
			break;
		}
			
		case REG_WIFI_BBWRITE: // 0x15A
			io.BB_WRITE.value = val & 0x00FF;
			break;
			
		case REG_WIFI_BBMODE: // 0x160
			io.BB_MODE.value = val & 0x0041;
			break;
			
		case REG_WIFI_BBPOWER: // 0x168
			io.BB_POWER.value = val & 0x800F;
			break;
			
		case 0x16A: // 0x16A
			io.INTERNAL_16A = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_16A\n", (int)io.INTERNAL_16A);
			break;
			
		case 0x170: // 0x170
			io.INTERNAL_170 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_170\n", (int)io.INTERNAL_170);
			break;
			
		case 0x172: // 0x172
			io.INTERNAL_172 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_172\n", (int)io.INTERNAL_172);
			break;
			
		case 0x174: // 0x174
			io.INTERNAL_174 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_174\n", (int)io.INTERNAL_174);
			break;
			
		case 0x176: // 0x176
			io.INTERNAL_176 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_176\n", (int)io.INTERNAL_176);
			break;
			
		case 0x178: // 0x178
			io.INTERNAL_178 = val;
			break;
			
		case REG_WIFI_RFDATA2: // 0x17C
		{
			RF2958_IOREG_MAP &rf = wifiHandler->GetWifiData().rf;
			
			io.RF_DATA2.value = val & 0x00FF;
			io.RF_BUSY.Busy = 1;
			
			const RegAddrRF2958 index = (RegAddrRF2958)io.RF_DATA2.Type2.Index;
			
			if (io.RF_DATA2.Type2.ReadCommand == 0) // Perform a write
			{
				switch (index)
				{
					case REG_RF2958_CFG1:
					case REG_RF2958_IPLL1:
					case REG_RF2958_IPLL2:
					case REG_RF2958_IPLL3:
					case REG_RF2958_RFPLL1:
					case REG_RF2958_RFPLL4:
					case REG_RF2958_CAL1:
					case REG_RF2958_TXRX1:
					case REG_RF2958_PCNT1:
					case REG_RF2958_PCNT2:
					case REG_RF2958_VCOT1:
						rf.reg[index].DataLSB = io.RF_DATA1.Type2.DataLSB;
						rf.reg[index].DataMSB = io.RF_DATA2.Type2.DataMSB;
						break;
						
					case REG_RF2958_RFPLL2:
					case REG_RF2958_RFPLL3:
					{
						u32 channelFreqN;
						
						rf.reg[index].DataLSB = io.RF_DATA1.Type2.DataLSB;
						rf.reg[index].DataMSB = io.RF_DATA2.Type2.DataMSB;
						
						// get the complete rfpll.n
						channelFreqN = (u32)rf.RFPLL3.NUM2 | ((u32)rf.RFPLL2.NUM2 << 18) | ((u32)rf.RFPLL2.N2 << 24);
						
						// frequency setting is out of range
						if (channelFreqN < 0x00A2E8BA)
						{
							break;
						}
						
						// substract base frequency (channel 1)
						channelFreqN -= 0x00A2E8BA;
						break;
					}
						
					case REG_RF2958_TEST:
						WIFI_LOG(2, "Writing value of %04Xh to RF test register\n", (int)rf.reg[index].Data);
						break;
						
					case REG_RF2958_RESET:
						WIFI_resetRF(rf);
						break;
						
					// Any unlisted register is assumed to be unused padding,
					// so just do nothing in this case.
					default:
						break;
				}
			}
			else // Perform a read
			{
				io.RF_DATA1.Type2.DataLSB = rf.reg[index].DataLSB;
				io.RF_DATA2.Type2.DataMSB = rf.reg[index].DataMSB;
			}
			
			// Normally, there should be some sort of transfer delay, but we're
			// not going to emulate the delay right now. Therefore, assume for
			// now that the transfer occurs instantaneously and clear the busy
			// flag accordingly.
			io.RF_BUSY.Busy = 0;
			break;
		}
			
		case REG_WIFI_RFDATA1: // 0x17E
			io.RF_DATA1.value = val;
			break;
			
		case REG_WIFI_RFCNT: // 0x184
		{
			if (io.RF_BUSY.Busy == 0)
			{
				IOREG_W_RF_CNT RF_CNT;
				RF_CNT.value = val;
				
				io.RF_CNT.TransferLen = RF_CNT.TransferLen;
				
				if (io.RF_CNT.UNKNOWN1 != RF_CNT.UNKNOWN1)
				{
					io.RF_CNT.UNKNOWN1 = RF_CNT.UNKNOWN1;
					WIFI_LOG(2, "Writing value of %d to RF_CNT.UNKNOWN1\n", (int)RF_CNT.UNKNOWN1);
				}
				
				if (io.RF_CNT.UNKNOWN2 != RF_CNT.UNKNOWN2)
				{
					io.RF_CNT.UNKNOWN2 = RF_CNT.UNKNOWN2;
					WIFI_LOG(2, "Writing value of %d to RF_CNT.UNKNOWN2\n", (int)RF_CNT.UNKNOWN2);
				}
			}
			break;
		}
			
		case 0x190: // 0x190
			io.INTERNAL_190 = val;
			break;
			
		case REG_WIFI_TX_HDR_CNT: // 0x194
			io.TX_HDR_CNT.value = val;
			break;
			
		case REG_WIFI_X_1A0: // 0x1A0
			io.X_1A0.value = val;
			WIFI_LOG(2, "Writing value of %04Xh to X_1A0\n", (int)io.X_1A0.value);
			break;
			
		case REG_WIFI_X_1A2: // 0x1A2
			io.X_1A2.value = val;
			WIFI_LOG(2, "Writing value of %04Xh to X_1A2\n", (int)io.X_1A2.value);
			break;
			
		case REG_WIFI_X_1A4: // 0x1A4
			io.X_1A4 = val;
			WIFI_LOG(2, "Writing value of %04Xh to X_1A4\n", (int)io.X_1A4);
			break;
			
		case REG_WIFI_RXSTAT_INC_IE: // 0x1AA
			io.RXSTAT_INC_IE.value = val;
			break;
			
		case REG_WIFI_RXSTAT_OVF_IE: // 0x1AE
			io.RXSTAT_OVF_IE.value = val;
			break;
			
		case REG_WIFI_RXSTAT0: // 0x1B0
			io.RXSTAT_COUNT.RXSTAT_1B0 = val;
			break;
			
		case REG_WIFI_RXSTAT1: // 0x1B2
			io.RXSTAT_COUNT.RXSTAT_1B2 = val;
			break;
			
		case REG_WIFI_RXSTAT2: // 0x1B4
			io.RXSTAT_COUNT.RXSTAT_1B4 = val;
			break;
			
		case REG_WIFI_RXSTAT3: // 0x1B6
			io.RXSTAT_COUNT.RXSTAT_1B6 = val;
			break;
			
		case REG_WIFI_RXSTAT4: // 0x1B8
			io.RXSTAT_COUNT.RXSTAT_1B8 = val;
			break;
			
		case REG_WIFI_RXSTAT5: // 0x1BA
			io.RXSTAT_COUNT.RXSTAT_1BA = val;
			break;
			
		case REG_WIFI_RXSTAT6: // 0x1BC
			io.RXSTAT_COUNT.RXSTAT_1BC = val;
			break;
			
		case REG_WIFI_RXSTAT7: // 0x1BE
			io.RXSTAT_COUNT.RXSTAT_1BE = val;
			break;
			
		case REG_WIFI_TXERR_COUNT: // 0x1C0
			io.TX_ERR_COUNT.Count++;
			break;
			
		case REG_WIFI_CMD_STAT0: // 0x1D0
			io.CMD_STAT_COUNT.CMD_STAT_1D0 = val;
			break;
			
		case REG_WIFI_CMD_STAT1: // 0x1D2
			io.CMD_STAT_COUNT.CMD_STAT_1D2 = val;
			break;
			
		case REG_WIFI_CMD_STAT2: // 0x1D4
			io.CMD_STAT_COUNT.CMD_STAT_1D4 = val;
			break;
			
		case REG_WIFI_CMD_STAT3: // 0x1D6
			io.CMD_STAT_COUNT.CMD_STAT_1D6 = val;
			break;
			
		case REG_WIFI_CMD_STAT4: // 0x1D8
			io.CMD_STAT_COUNT.CMD_STAT_1D8 = val;
			break;
			
		case REG_WIFI_CMD_STAT5: // 0x1DA
			io.CMD_STAT_COUNT.CMD_STAT_1DA = val;
			break;
			
		case REG_WIFI_CMD_STAT6: // 0x1DC
			io.CMD_STAT_COUNT.CMD_STAT_1DC = val;
			break;
			
		case REG_WIFI_CMD_STAT7: // 0x1DE
			io.CMD_STAT_COUNT.CMD_STAT_1DE = val;
			break;
			
		case 0x1F0: // 0x1F0
			io.INTERNAL_1F0 = val;
			break;
			
		case 0x204: // 0x204
			io.INTERNAL_204 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_204\n", (int)io.INTERNAL_204);
			break;
			
		case 0x208: // 0x208
			io.INTERNAL_208 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_208\n", (int)io.INTERNAL_208);
			break;
			
		case 0x20C: // 0x20C
			io.INTERNAL_20C = val;
			break;
			
		case REG_WIFI_IF_SET: // 0x21C
			WIFI_triggerIRQMask(val);
			break;
			
		case 0x220: // 0x220
			io.INTERNAL_220 = val;
			break;
			
		case 0x224: // 0x224
			io.INTERNAL_224 = val;
			break;
			
		case REG_WIFI_X_228: // 0x228
			io.X_228 = val;
			break;
			
		case 0x230: // 0x230
			io.INTERNAL_230 = val;
			break;
			
		case 0x234: // 0x234
			io.INTERNAL_234 = val;
			break;
			
		case 0x238: // 0x238
			io.INTERNAL_238 = val;
			break;
			
		case 0x23C: // 0x23C
			io.INTERNAL_23C = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_23C\n", (int)io.INTERNAL_23C);
			break;
			
		case REG_WIFI_X_244: // 0x244
			io.X_244 = val;
			break;
			
		case 0x248: // 0x248
			io.INTERNAL_248 = val;
			break;
			
		case REG_WIFI_CONFIG_254: // 0x254
			io.CONFIG_254 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only CONFIG_254\n", (int)io.CONFIG_254);
			break;
			
		case 0x258: // 0x258
			io.INTERNAL_258 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_258\n", (int)io.INTERNAL_258);
			break;
			
		case 0x25C: // 0x25C
			io.INTERNAL_25C = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_25C\n", (int)io.INTERNAL_25C);
			break;
			
		case 0x260: // 0x260
			io.INTERNAL_260 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_260\n", (int)io.INTERNAL_260);
			break;
			
		case 0x274: // 0x274
			io.INTERNAL_274 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_274\n", (int)io.INTERNAL_274);
			break;
			
		case 0x278: // 0x278
			io.INTERNAL_278 = val;
			break;
			
		case 0x27C: // 0x27C
			io.INTERNAL_27C = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_27C\n", (int)io.INTERNAL_27C);
			break;
			
		case REG_WIFI_X_290: // 0x290
			io.X_290.value = val;
			break;
			
		case 0x298: // 0x298
			io.INTERNAL_298 = val;
			break;
			
		case 0x2A0: // 0x2A0
			io.INTERNAL_2A0 = val;
			break;
			
		case 0x2A8: // 0x2A8
			io.INTERNAL_2A8 = val;
			break;
			
		case 0x2AC: // 0x2AC
			io.INTERNAL_2AC = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_2AC\n", (int)io.INTERNAL_2AC);
			break;
			
		case 0x2B0: // 0x2B0
			io.INTERNAL_2B0 = val;
			break;
			
		case 0x2B4: // 0x2B4
			io.INTERNAL_2B4 = val;
			break;
			
		case 0x2B8: // 0x2B8
			io.INTERNAL_2B8 = val;
			WIFI_LOG(2, "Writing value of %04Xh to possible read-only INTERNAL_2B8\n", (int)io.INTERNAL_2B8);
			break;
			
		case 0x2C0: // 0x2C0
			io.INTERNAL_2C0 = val;
			break;
			
		case REG_WIFI_POWERACK: // 0x2D0
		{
			if (io.POWERSTATE.WillPowerOn != 0)
			{
				io.POWERACK = val;
				
				if (io.POWERACK == 0x0000)
				{
					io.POWERSTATE.WillPowerOn = 0;
					io.POWERSTATE.IsPowerOff = io.POWERFORCE.NewPowerOffState;
					
					if (io.POWERSTATE.IsPowerOff == 0)
					{
						io.POWERSTATE.RequestPowerOn = 0;
						WIFI_triggerIRQ(WIFI_IRQ_RFWAKEUP);
						
						// This is supposedly the TX beacon transfer flag.
						// Since reading TXREQ_READ bit 4 should always return a 1 (unless
						// POWERFORCE clears it), we'll simply set this bit to 1 on power up
						// so that bit 4 gets its default value.
						io.TXREQ_READ.UNKNOWN1 = 1;
					}
					else
					{
						io.RF_STATUS.RFStatus = 0x9;
						io.RF_PINS.CarrierSense = 0;
						io.RF_PINS.TXMain = 1;
						io.RF_PINS.UNKNOWN1 = 1;
						io.RF_PINS.TX_On = 1;
						io.RF_PINS.RX_On = 0;
						
						io.INTERNAL_034 = 0x0002;
						io.TXREQ_READ.value &= 0x0010;
						io.POWERSTATE.WillPowerOn = 0;
						io.POWERSTATE.IsPowerOff = 1;
					}
				}
			}
			break;
		}
			
		case 0x2F0: // 0x2F0
			io.INTERNAL_2F0 = val;
			break;
			
		case 0x2F2: // 0x2F2
			io.INTERNAL_2F2 = val;
			break;
			
		case 0x2F4: // 0x2F4
			io.INTERNAL_2F4 = val;
			break;
			
		case 0x2F6: // 0x2F6
			io.INTERNAL_2F6 = val;
			break;
			
		// Report any writes to read-only registers because this shouldn't happen.
		case REG_WIFI_ID:						// 0x000
		case REG_WIFI_RANDOM:					// 0x044
		case REG_WIFI_RXHWWRITECSR:				// 0x054
		case REG_WIFI_CIRCBUFREAD:				// 0x060
		case REG_WIFI_TXBUF_REPLY2:				// 0x098
		case REG_WIFI_TXREQ_READ:				// 0x0B0
		case REG_WIFI_TXBUSY:					// 0x0B6
		case REG_WIFI_TXSTAT:					// 0x0B8
		case REG_WIFI_BBREAD:					// 0x15C
		case REG_WIFI_BBBUSY:					// 0x15E
		case REG_WIFI_RFBUSY:					// 0x180
		case REG_WIFI_RFPINS:					// 0x19C
		case REG_WIFI_RXSTAT_INC_IF:			// 0x1A8
		case REG_WIFI_RXSTAT_OVF_IF:			// 0x1AC
		case REG_WIFI_RX_COUNT:					// 0x1C4
		case REG_WIFI_TXSEQNO:					// 0x210
		case REG_WIFI_RFSTATUS:					// 0x214
		case 0x24C:								// 0x24C
		case 0x24E:								// 0x24E
		case 0x250:								// 0x250
		case 0x264:								// 0x264
		case REG_WIFI_RXTX_ADDR:				// 0x268
		case 0x270:								// 0x270
		case 0x2A2:								// 0x2A2
		case 0x2A4:								// 0x2A4
		case 0x2C4:								// 0x2C4
		case 0x2C8:								// 0x2C8
		case 0x2CC:								// 0x2CC
			WIFI_LOG(2, "Preventing writing value of %04Xh to read-only port 0x%03X\n", val, address);
			break;
			
		// Assume that any unlisted register is just padding. It's meaningless to
		// write to padding, so report that here.
		default:
			WIFI_LOG(2, "Preventing writing value of %04Xh to padding address 0x%03X\n", val, address);
			break;
	}
}

u16 WIFI_read16(u32 address)
{
	bool action = false;
	if (!nds.power2.wifi) return 0;
	
	WifiData &wifi = wifiHandler->GetWifiData();
	WIFI_IOREG_MAP &io = wifi.io;

	u32 page = address & 0x7000;

	// 0x2000 - 0x3FFF: unused
	if ((page >= 0x2000) && (page < 0x4000))
		return 0xFFFF;

	WIFI_LOG(5, "Read at address %08X\n", address);

	// 0x4000 - 0x5FFF: wifi RAM
	if ((page >= 0x4000) && (page < 0x6000))
	{
        return wifi.RAM[(address & 0x1FFF) >> 1];
	}

	// anything else: I/O ports
	// only the first mirror causes a special action
	if (page == 0x0000) action = true;
	
	address &= 0x0FFF;
	switch (address)
	{
		case REG_WIFI_ID: // 0x000
			return WIFI_CHIPID;
			
		case REG_WIFI_MODE: // 0x004
			return io.MODE_RST.value;
			
		case REG_WIFI_WEP: // 0x006
			return io.MODE_WEP.value;
			
		case REG_WIFI_TXSTATCNT: // 0x008
			return io.TXSTATCNT.value;
			
		case REG_WIFI_X_00A: // 0x00A
			return io.X_00A;
			
		case REG_WIFI_IF: // 0x010
			return io.IF.value;
			
		case REG_WIFI_IE: // 0x012
			return io.IE.value;
			
		case REG_WIFI_MAC0: // 0x018
			return io.MACADDR0.value;
			
		case REG_WIFI_MAC1: // 0x01A
			return io.MACADDR1.value;
			
		case REG_WIFI_MAC2: // 0x01C
			return io.MACADDR2.value;
			
		case REG_WIFI_BSS0: // 0x020
			return io.BSSID0.value;
			
		case REG_WIFI_BSS1: // 0x022
			return io.BSSID1.value;
			
		case REG_WIFI_BSS2: // 0x024
			return io.BSSID2.value;
			
		case REG_WIFI_AID_LOW: // 0x028
			return io.AID_LOW.value;
			
		case REG_WIFI_AID_HIGH: // 0x02A
			return io.AID_FULL.value;
			
		case REG_WIFI_RETRYLIMIT: // 0x02C
			return io.TX_RETRYLIMIT.value;
			
		case 0x2E: // 0x02E
			return io.INTERNAL_02E;
			
		case REG_WIFI_RXCNT: // 0x030
			return io.RXCNT.value;
			
		case REG_WIFI_WEPCNT: // 0x032
			return io.WEP_CNT.value;
			
		case 0x034: // 0x034
			return io.INTERNAL_034;
			
		case REG_WIFI_POWER_US: // 0x036
			return io.POWER_US.value;
			
		case REG_WIFI_POWER_TX: // 0x038
			return io.POWER_TX.value;
			
		case REG_WIFI_POWERSTATE: // 0x03C
			return io.POWERSTATE.value;
			
		case REG_WIFI_POWERFORCE: // 0x040
			return io.POWERFORCE.value;
			
		case REG_WIFI_RANDOM: // 0x044
		{
			u16 returnValue = io.RANDOM.Random;
			io.RANDOM.Random = (io.RANDOM.Random & 1) ^ (((io.RANDOM.Random << 1) & 0x7FE) | ((io.RANDOM.Random >> 10) & 0x1));
			return returnValue;
		}
			
		case REG_WIFI_POWER_UNK: // 0x048
			return io.POWER_UNKNOWN.value;
			
		case REG_WIFI_RXRANGEBEGIN: // 0x050
			return io.RXBUF_BEGIN;
			
		case REG_WIFI_RXRANGEEND: // 0x052
			return io.RXBUF_END;
			
		case REG_WIFI_RXHWWRITECSR: // 0x054
			return io.RXBUF_WRCSR.value;
			
		case REG_WIFI_WRITECSRLATCH: // 0x056
			return io.RXBUF_WR_ADDR.value;
			
		case REG_WIFI_CIRCBUFRADR: // 0x058
			return io.RXBUF_RD_ADDR.value;
			
		case REG_WIFI_RXREADCSR: // 0x05A
			return io.RXBUF_READCSR.value;
			
		case REG_WIFI_RXBUF_COUNT: // 0x05C
			return io.RXBUF_COUNT.value;
			
		case REG_WIFI_CIRCBUFREAD: // 0x060
		case REG_WIFI_CIRCBUFWRITE: // 0x070 - mirrored read of 0x060
		{
			u16 val = wifi.RAM[io.RXBUF_RD_ADDR.HalfwordAddress];
			
			if (action)
			{
				io.RXBUF_RD_ADDR.HalfwordAddress++;
				
				if (io.RXBUF_RD_ADDR.ByteAddress >= io.RXBUF_END)
				{
					io.RXBUF_RD_ADDR.ByteAddress = io.RXBUF_BEGIN;
				}
				else
				{
					/* skip does not fire after a reset */
					if (io.RXBUF_RD_ADDR.HalfwordAddress == io.RXBUF_GAP.HalfwordAddress)
					{
						io.RXBUF_RD_ADDR.HalfwordAddress += io.RXBUF_GAPDISP.HalfwordOffset;
						if (io.RXBUF_RD_ADDR.ByteAddress + io.RXBUF_BEGIN == io.RXBUF_END) io.RXBUF_RD_ADDR.ByteAddress = 0;
					}
				}
				
				if (io.RXBUF_COUNT.Count > 0)
				{
					if (io.RXBUF_COUNT.Count == 1)
					{
						WIFI_triggerIRQ(WIFI_IRQ_RXCOUNTEXP);
					}
					
					io.RXBUF_COUNT.Count--;
				}
			}
			return val;
		}
			
		case REG_WIFI_CIRCBUFRD_END: // 0x062
			return io.RXBUF_GAP.value;
			
		case REG_WIFI_CIRCBUFRD_SKIP: // 0x064
			return io.RXBUF_GAPDISP.value;
			
		case REG_WIFI_CIRCBUFWADR: // 0x068
			return io.TXBUF_WR_ADDR.value;
			
		case REG_WIFI_TXBUFCOUNT: // 0x06C
			return io.TXBUF_COUNT.value;
			
		case REG_WIFI_CIRCBUFWR_END: // 0x074
			return io.TXBUF_GAP.value;
			
		case REG_WIFI_CIRCBUFWR_SKIP: // 0x076
			return io.TXBUF_GAPDISP.value;
			
		case 0x078: // 0x078 - mirrored read of 0x068
			return io.TXBUF_WR_ADDR.value;
			
		case REG_WIFI_TXLOCBEACON: // 0x080
			return io.TXBUF_BEACON.value;
			
		case REG_WIFI_TXBUF_TIM: // 0x084
			return io.TXBUF_TIM.value;
			
		case REG_WIFI_LISTENCOUNT: // 0x088
			return io.LISTENCOUNT.value;
			
		case REG_WIFI_BEACONPERIOD: // 0x08C
			return io.BEACONINT.value;
			
		case REG_WIFI_LISTENINT: // 0x08E
			return io.LISTENINT.value;
			
		case REG_WIFI_TXLOCEXTRA: // 0x090
			return io.TXBUF_CMD.value;
			
		case REG_WIFI_TXBUF_REPLY1: // 0x094
			return io.TXBUF_REPLY1.value;
			
		case REG_WIFI_TXBUF_REPLY2: // 0x098
			return io.TXBUF_REPLY2.value;
			
		case 0x09C: // 0x09C
			return io.INTERNAL_09C;
			
		case REG_WIFI_TXLOC1: // 0x0A0
			return io.TXBUF_LOC1.value;
			
		case REG_WIFI_TXLOC2: // 0x0A4
			return io.TXBUF_LOC2.value;
			
		case REG_WIFI_TXLOC3: // 0x0A8
			return io.TXBUF_LOC3.value;
			
		case REG_WIFI_TXREQ_RESET: // 0x0AC - mirrored read of 0x09C
		case REG_WIFI_TXREQ_SET: // 0x0AE - mirrored read of 0x09C
			return io.INTERNAL_09C;
			
		case REG_WIFI_TXREQ_READ: // 0x0B0
			return io.TXREQ_READ.value;
			
		case REG_WIFI_TXRESET: // 0x0B4 - mirrored read of 0x0B6
		case REG_WIFI_TXBUSY: // 0x0B6
			return io.TXBUSY.value;
			
		case REG_WIFI_TXSTAT: // 0x0B8
			return io.TXSTAT.value;
			
		case 0x0BA: // 0x0BA
			return io.INTERNAL_0BA;
			
		case REG_WIFI_PREAMBLE: // 0x0BC
			return io.PREAMBLE.value;
			
		case REG_WIFI_CMD_TOTALTIME: // 0x0C0
			return io.CMD_TOTALTIME;
			
		case REG_WIFI_CMD_REPLYTIME: // 0x0C4
			return io.CMD_REPLYTIME;
			
		case 0x0C8: // 0x0C8
			return io.INTERNAL_0C8;
			
		case REG_WIFI_RXFILTER: // 0x0D0
			return io.RXFILTER.value;
			
		case REG_WIFI_CONFIG_0D4: // 0x0D4
			return io.CONFIG_0D4;
			
		case REG_WIFI_CONFIG_0D8: // 0x0D8
			return io.CONFIG_0D8;
			
		case REG_WIFI_RX_LEN_CROP: // 0x0DA
			return io.RX_LEN_CROP.value;
			
		case REG_WIFI_RXFILTER2: // 0x0E0
			return io.RXFILTER2.value;
			
		case REG_WIFI_USCOUNTERCNT: // 0x0E8
			return io.US_COUNTCNT.value;
			
		case REG_WIFI_USCOMPARECNT: // 0x0EA
			return io.US_COMPARECNT.value;
			
		case REG_WIFI_CONFIG_0EC: // 0x0EC
			return io.CONFIG_0EC;
			
		case REG_WIFI_EXTRACOUNTCNT: // 0x0EE
			return io.CMD_COUNTCNT.value;
			
		case REG_WIFI_USCOMPARE0: // 0x0F0
			return io.US_COMPARE0;
			
		case REG_WIFI_USCOMPARE1: // 0x0F2
			return io.US_COMPARE1;
			
		case REG_WIFI_USCOMPARE2: // 0x0F4
			return io.US_COMPARE2;
			
		case REG_WIFI_USCOMPARE3: // 0x0F6
			return io.US_COMPARE3;
			
		case REG_WIFI_USCOUNTER0: // 0x0F8
			return io.US_COUNT0;
			
		case REG_WIFI_USCOUNTER1: // 0x0FA
			return io.US_COUNT1;
			
		case REG_WIFI_USCOUNTER2: // 0x0FC
			return io.US_COUNT2;
			
		case REG_WIFI_USCOUNTER3: // 0x0FE
			return io.US_COUNT3;
			
		case 0x100: // 0x100
			return io.INTERNAL_100;
			
		case 0x102: // 0x102
			return io.INTERNAL_102;
			
		case 0x104: // 0x104
			return io.INTERNAL_104;
			
		case 0x106: // 0x106
			return io.INTERNAL_106;
			
		case REG_WIFI_CONTENTFREE: // 0x10C
			return io.CONTENTFREE;
			
		case REG_WIFI_PREBEACONCOUNT: // 0x110
			return io.PRE_BEACON;
			
		case REG_WIFI_EXTRACOUNT: // 0x118
			return (u16)((wifi.cmdCount_u32 + 9) / 10);
			
		case REG_WIFI_BEACONCOUNT1: // 0x11C
			return io.BEACONCOUNT1;
			
		case REG_WIFI_CONFIG_120: // 0x120
			return io.CONFIG_120;
			
		case REG_WIFI_CONFIG_122: // 0x122
			return io.CONFIG_122;
			
		case REG_WIFI_CONFIG_124: // 0x124
			return io.CONFIG_124;
			
		case 0x126: // 0x126
			return io.INTERNAL_126;
			
		case REG_WIFI_CONFIG_128: // 0x128
			return io.CONFIG_128;
			
		case 0x12A: // 0x12A
			return io.INTERNAL_12A;
			
		case REG_WIFI_CONFIG_130: // 0x130
			return io.CONFIG_130;
			
		case REG_WIFI_CONFIG_132: // 0x132
			return io.CONFIG_132;
			
		case REG_WIFI_BEACONCOUNT2: // 0x134
			return io.BEACONCOUNT2;
			
		case REG_WIFI_CONFIG_140: // 0x140
			return io.CONFIG_140;
			
		case REG_WIFI_CONFIG_142: // 0x142
			return io.CONFIG_142;
			
		case REG_WIFI_CONFIG_144: // 0x144
			return io.CONFIG_144;
			
		case REG_WIFI_CONFIG_146: // 0x146
			return io.CONFIG_146;
			
		case REG_WIFI_CONFIG_148: // 0x148
			return io.CONFIG_148;
			
		case REG_WIFI_CONFIG_14A: // 0x14A
			return io.CONFIG_14A;
			
		case REG_WIFI_CONFIG_14C: // 0x14C
			return io.CONFIG_14C;
			
		case REG_WIFI_CONFIG_150: // 0x150
			return io.CONFIG_150;
			
		case REG_WIFI_CONFIG_154: // 0x154
			return io.CONFIG_154;
			
		case REG_WIFI_BBCNT: // 0x158 - mirrored read of 0x15C
			return io.BB_READ.value;
			
		case REG_WIFI_BBWRITE: // 0x15A
			return 0;
			
		case REG_WIFI_BBREAD: // 0x15C
			return io.BB_READ.value;
			
		case REG_WIFI_BBBUSY: // 0x15E
			return io.BB_BUSY.value;
			
		case REG_WIFI_BBMODE: // 0x160
			return io.BB_MODE.value;
			
		case REG_WIFI_BBPOWER: // 0x168
			return io.BB_POWER.value;
			
		case 0x16A: // 0x16A
			return io.INTERNAL_16A;
			
		case 0x170: // 0x170
			return io.INTERNAL_170;
			
		case 0x172: // 0x172
			return io.INTERNAL_172;
			
		case 0x174: // 0x174
			return io.INTERNAL_174;
			
		case 0x176: // 0x176
			return io.INTERNAL_176;
			
		case 0x178: // 0x178 - mirrored read of 0x17C
		case REG_WIFI_RFDATA2: // 0017C
		{
			if (io.RF_BUSY.Busy == 0)
			{
				return io.RF_DATA2.value;
			}
			else
			{
				return 0;
			}
		}
			
		case REG_WIFI_RFDATA1: // 0x17E
		{
			if (io.RF_BUSY.Busy == 0)
			{
				return io.RF_DATA1.value;
			}
			else
			{
				return 0;
			}
		}
			
		case REG_WIFI_RFBUSY: // 0x180
			return io.RF_BUSY.value;
			
		case REG_WIFI_RFCNT: // 0x184
			return io.RF_CNT.value;
			
		case 0x190: // 0x190
			return io.INTERNAL_190;
			
		case REG_WIFI_TX_HDR_CNT: // 0x194
			return io.TX_HDR_CNT.value;
			
		case 0x198: // 0x198
			return io.INTERNAL_198;
			
		case REG_WIFI_X_1A0: // 0x1A0
			return io.X_1A0.value;
			
		case REG_WIFI_X_1A2: // 0x1A2
			return io.X_1A2.value;
			
		case REG_WIFI_X_1A4: // 0x1A4
			return io.X_1A4;
			
		case REG_WIFI_RXSTAT_INC_IF: // 0x1A8
			return io.RXSTAT_INC_IF.value;
			
		case REG_WIFI_RXSTAT_INC_IE: // 0x1AA
			return io.RXSTAT_INC_IE.value;
			
		case REG_WIFI_RXSTAT_OVF_IF: // 0x1AC
			return io.RXSTAT_OVF_IF.value;
			
		case REG_WIFI_RXSTAT_OVF_IE: // 0x1AE
			return io.RXSTAT_OVF_IE.value;
			
		case REG_WIFI_RXSTAT0: // 0x1B0
			return io.RXSTAT_COUNT.RXSTAT_1B0;
			
		case REG_WIFI_RXSTAT1: // 0x1B2
			return io.RXSTAT_COUNT.RXSTAT_1B2;
			
		case REG_WIFI_RXSTAT2: // 0x1B4
			return io.RXSTAT_COUNT.RXSTAT_1B4;
			
		case REG_WIFI_RXSTAT3: // 0x1B6
			return io.RXSTAT_COUNT.RXSTAT_1B6;
			
		case REG_WIFI_RXSTAT4: // 0x1B8
			return io.RXSTAT_COUNT.RXSTAT_1B8;
			
		case REG_WIFI_RXSTAT5: // 0x1BA
			return io.RXSTAT_COUNT.RXSTAT_1BA;
			
		case REG_WIFI_RXSTAT6: // 0x1BC
			return io.RXSTAT_COUNT.RXSTAT_1BC;
			
		case REG_WIFI_RXSTAT7: // 0x1BE
			return io.RXSTAT_COUNT.RXSTAT_1BE;
			
		case REG_WIFI_TXERR_COUNT: // 0x1C0
			return io.TX_ERR_COUNT.value;
			
		case REG_WIFI_RX_COUNT: // 0x1C4
			return io.RX_COUNT.value;
			
		case REG_WIFI_CMD_STAT0: // 0x1D0
			return io.CMD_STAT_COUNT.CMD_STAT_1D0;
			
		case REG_WIFI_CMD_STAT1: // 0x1D2
			return io.CMD_STAT_COUNT.CMD_STAT_1D2;
			
		case REG_WIFI_CMD_STAT2: // 0x1D4
			return io.CMD_STAT_COUNT.CMD_STAT_1D4;
			
		case REG_WIFI_CMD_STAT3: // 0x1D6
			return io.CMD_STAT_COUNT.CMD_STAT_1D6;
			
		case REG_WIFI_CMD_STAT4: // 0x1D8
			return io.CMD_STAT_COUNT.CMD_STAT_1D8;
			
		case REG_WIFI_CMD_STAT5: // 0x1DA
			return io.CMD_STAT_COUNT.CMD_STAT_1DA;
			
		case REG_WIFI_CMD_STAT6: // 0x1DC
			return io.CMD_STAT_COUNT.CMD_STAT_1DC;
			
		case REG_WIFI_CMD_STAT7: // 0x1DE
			return io.CMD_STAT_COUNT.CMD_STAT_1DE;
			
		case 0x1F0: // 0x1F0
			return io.INTERNAL_1F0;
			
		case 0x204: // 0x204
			return io.INTERNAL_204;
			
		case 0x208: // 0x208
			return io.INTERNAL_208;
			
		case 0x20C: // 0x20C - mirrored read of 0x09C
			return io.INTERNAL_09C;
			
		case REG_WIFI_TXSEQNO: // 0x210
			return io.TX_SEQNO.value;
			
		case REG_WIFI_IF_SET: // 0x21C - mirrored read of 0x010
			return io.IF.value;
			
		case 0x220: // 0x220
			return io.INTERNAL_220;
			
		case 0x224: // 0x224
			return io.INTERNAL_224;
			
		case REG_WIFI_X_228: // 0x228
			return 0;
			
		case 0x230: // 0x230
			return io.INTERNAL_230;
			
		case 0x234: // 0x234
			return io.INTERNAL_234;
			
		case 0x238: // 0x238
			return io.INTERNAL_238;
			
		case 0x23C: // 0x23C
			return io.INTERNAL_23C;
			
		case REG_WIFI_X_244: // 0x244
			return io.X_244;
			
		case 0x248: // 0x248
			return io.INTERNAL_248;
			
		case 0x24C: // 0x24C
			return io.INTERNAL_24C;
			
		case 0x24E: // 0x24E
			return io.INTERNAL_24E;
			
		case 0x250: // 0x250
			return io.INTERNAL_250;
			
		case REG_WIFI_CONFIG_254: // 0x254
			return io.CONFIG_254;
			
		case 0x258: // 0x258
			return io.INTERNAL_258;
			
		case 0x25C: // 0x25C
			return io.INTERNAL_25C;
			
		case 0x260: // 0x260
			return io.INTERNAL_260;
			
		case 0x264: // 0x264
			return io.INTERNAL_264;
			
		case REG_WIFI_RXTX_ADDR: // 0x268
			return io.RXTX_ADDR.value;
			
		case 0x270: // 0x270
			return io.INTERNAL_270;
			
		case 0x274: // 0x274
			return io.INTERNAL_274;
			
		case 0x278: // 0x278
			return io.INTERNAL_278;
			
		case 0x27C: // 0x27C
			return io.INTERNAL_27C;
			
		case REG_WIFI_X_290: // 0x290
			return io.X_290.value;
			
		case 0x298: // 0x298 - mirrored read of 0x084
			return io.TXBUF_TIM.value;
			
		case 0x2A0: // 0x2A0
			return io.INTERNAL_2A0;
			
		case 0x2A2: // 0x2A2
			return io.INTERNAL_2A2;
			
		case 0x2A4: // 0x2A4
			return io.INTERNAL_2A4;
			
		case 0x2A8: // 0x2A8 - mirrored read of 0x238
			return io.INTERNAL_238;
			
		case 0x2AC: // 0x2AC
			return io.INTERNAL_2AC;
			
		case 0x2B0: // 0x2B0 - mirrored read of 0x084
			return io.TXBUF_TIM.value;
			
		case 0x2B4: // 0x2B4
			return io.INTERNAL_2B4;
			
		case 0x2B8: // 0x2B8
			return io.INTERNAL_2B8;
			
		case 0x2C0: // 0x2C0
			return io.INTERNAL_2C0;
			
		case 0x2C4: // 0x2C4
			return io.INTERNAL_2C4;
			
		case 0x2C8: // 0x2C8
			return io.INTERNAL_2C8;
			
		case 0x2CC: // 0x2CC
			return io.INTERNAL_2CC;
			
		case REG_WIFI_POWERACK: // 0x2D0
		{
			if (io.POWERSTATE.WillPowerOn != 0)
			{
				return io.POWERACK;
			}
			else
			{
				break;
			}
		}
			
		case 0x2F0: // 0x2F0
			return io.INTERNAL_2F0;
			
		case 0x2F2: // 0x2F2
			return io.INTERNAL_2F2;
			
		case 0x2F4: // 0x2F4
			return io.INTERNAL_2F4;
			
		case 0x2F6: // 0x2F6
			return io.INTERNAL_2F6;
			
		// RFSTATUS, RFPINS
		// TODO: figure out how to emulate those correctly
		// without breaking Nintendo's games
		case REG_WIFI_RFSTATUS: // 0x214
			return 0x0009;
		case REG_WIFI_RFPINS: // 0x19C
			return 0x00C6;

		default:
		//	printf("wifi: read unhandled reg %03X\n", address);
			break;
	}
	
	// TODO: We return the default value for the original NDS. However, the default value is different for NDS Lite.
	WIFI_LOG(2, "Reading value of %04Xh from unlabeled register 0x%03X\n", 0xFFFF, address);
	return 0xFFFF;
}


void WIFI_usTrigger()
{
	WifiData &wifi = wifiHandler->GetWifiData();
	WIFI_IOREG_MAP &io = wifi.io;
	
	if (io.POWER_US.Disable == 0)
	{
		/* a usec has passed */
		if (io.US_COUNTCNT.EnableCounter != 0)
		{
			io.US_COUNT++;
		}
		
		// Note: the extra counter is decremented every 10 microseconds.
		// To avoid a modulo every microsecond, we multiply the counter
		// value by 10 and decrement it every microsecond :)
		if (io.CMD_COUNTCNT.EnableCounter != 0)
		{
			if (wifi.cmdCount_u32 > 0)
			{
				wifi.cmdCount_u32--;
				
				if (wifi.cmdCount_u32 == 0)
				{
					WIFI_TXStart(WifiTXLocIndex_CMD, io.TXBUF_CMD);
				}
			}
		}

		// The beacon counters are in milliseconds
		// GBATek says they're decremented every 1024 usecs
		if ( !(io.US_COUNT & 1023) )
		{
			io.BEACONCOUNT1--;

			if ( io.BEACONCOUNT1 == (io.PRE_BEACON >> 10) )
			{
				WIFI_triggerIRQ(WIFI_IRQ_TIMEPREBEACON);
			}
			else if (io.BEACONCOUNT1 == 0)
			{
				WIFI_triggerIRQ(WIFI_IRQ_TIMEBEACON);
			}

			if (io.BEACONCOUNT2 > 0)
			{
				io.BEACONCOUNT2--;
				
				if (io.BEACONCOUNT2 == 0)
				{
					WIFI_triggerIRQ(WIFI_IRQ_TIMEPOSTBEACON);
				}
			}
		}
	}

	if ( (io.US_COMPARECNT.EnableCompare != 0) && (io.US_COMPARE == io.US_COUNT) )
	{
		//printf("ucmp irq14\n");
		WIFI_triggerIRQ(WIFI_IRQ_TIMEBEACON);
	}

	if ((io.US_COUNT & 3) == 0)
	{
		const WifiTXLocIndex txSlotIndex = wifi.txCurrentSlot;
		bool isTXSlotBusy = false;
		
		switch (txSlotIndex)
		{
			case WifiTXLocIndex_LOC1: isTXSlotBusy = (io.TXBUSY.Loc1 != 0); break;
			//case WifiTXLocIndex_CMD: isTXSlotBusy = (io.TXBUSY.Cmd != 0); break;
			case WifiTXLocIndex_LOC2: isTXSlotBusy = (io.TXBUSY.Loc2 != 0); break;
			case WifiTXLocIndex_LOC3: isTXSlotBusy = (io.TXBUSY.Loc3 != 0); break;
			//case WifiTXLocIndex_BEACON: isTXSlotBusy = (io.TXBUSY.Beacon != 0); break;
				
			default:
				break;
		}

		if (isTXSlotBusy)
		{
			IOREG_W_TXBUF_LOCATION *txBufLocation = NULL;
			TXPacketInfo &txPacketInfo = wifiHandler->GetPacketInfoAtSlot(txSlotIndex);
			
			switch (txSlotIndex)
			{
				case WifiTXLocIndex_LOC1: txBufLocation = &io.TXBUF_LOC1; break;
				//case WifiTXLocIndex_CMD: txBufLocation = &io.TXBUF_CMD; break;
				case WifiTXLocIndex_LOC2: txBufLocation = &io.TXBUF_LOC2; break;
				case WifiTXLocIndex_LOC3: txBufLocation = &io.TXBUF_LOC3; break;
				//case WifiTXLocIndex_BEACON: txBufLocation = &io.TXBUF_BEACON; break;
					
				default:
					break;
			}
			
			txPacketInfo.remainingBytes--;
			io.RXTX_ADDR.HalfwordAddress++;
			
			if (txPacketInfo.remainingBytes == 0)
			{
				isTXSlotBusy = false;
				
				switch (txSlotIndex)
				{
					case WifiTXLocIndex_LOC1: io.TXBUSY.Loc1 = 0; break;
					//case WifiTXLocIndex_CMD: io.TXBUSY.Cmd = 0; break;
					case WifiTXLocIndex_LOC2: io.TXBUSY.Loc2 = 0; break;
					case WifiTXLocIndex_LOC3: io.TXBUSY.Loc3 = 0; break;
					//case WifiTXLocIndex_BEACON: io.TXBUSY.Beacon = 0; break;
						
					default:
						break;
				}
				
				txBufLocation->TransferRequest = 0;
				wifiHandler->CommSendPacket((u8 *)&wifi.RAM[txBufLocation->HalfwordAddress+6], txPacketInfo.bodyLen);
				
				wifi.RAM[txBufLocation->HalfwordAddress] = 0x0001;
				wifi.RAM[txBufLocation->HalfwordAddress+4] &= 0x00FF;
				
				switch (txSlotIndex)
				{
					//case WifiTXLocIndex_CMD:
					//case WifiTXLocIndex_BEACON:
					case WifiTXLocIndex_LOC1: io.TXSTAT.PacketUpdate = 0; break;
					case WifiTXLocIndex_LOC2: io.TXSTAT.PacketUpdate = 1; break;
					case WifiTXLocIndex_LOC3: io.TXSTAT.PacketUpdate = 2; break;
						
					default:
						break;
				}
				
				io.TXSTAT.PacketCompleted = 1;
				
				WIFI_triggerIRQ(WIFI_IRQ_TXEND);
				
				//io.RF_STATUS.RFStatus = 0x01;
				//io.RF_PINS.RX_On = 1;
				
				io.RF_STATUS.RFStatus = 0x09;
				io.RF_PINS.CarrierSense = 0;
				io.RF_PINS.TXMain = 0;
				io.RF_PINS.UNKNOWN1 = 1;
				io.RF_PINS.TX_On = 0;
				io.RF_PINS.RX_On = 0;
				
				// Switch to the next TX slot.
				while (!isTXSlotBusy && (wifi.txCurrentSlot != WifiTXLocIndex_LOC1))
				{
					/*if (wifi.txCurrentSlot == WifiTXLocIndex_BEACON)
					 {
					 wifi.txCurrentSlot = WifiTXLocIndex_CMD;
					 isTXSlotBusy = (io.TXBUSY.Cmd != 0);
					 }
					 else if (wifi.txCurrentSlot == WifiTXLocIndex_CMD)
					 {
					 wifi.txCurrentSlot = WifiTXLocIndex_LOC3;
					 isTXSlotBusy = (io.TXBUSY.Loc3 != 0);
					 }
					 else */if (wifi.txCurrentSlot == WifiTXLocIndex_LOC3)
					 {
						 wifi.txCurrentSlot = WifiTXLocIndex_LOC2;
						 isTXSlotBusy = (io.TXBUSY.Loc2 != 0);
					 }
					 else if (wifi.txCurrentSlot == WifiTXLocIndex_LOC2)
					 {
						 wifi.txCurrentSlot = WifiTXLocIndex_LOC1;
						 isTXSlotBusy = (io.TXBUSY.Loc1 != 0);
					 }
				}

				WIFI_LOG(3, "TX slot %i finished sending its packet. Next is slot %i. TXStat = %04X\n", 
					(int)txSlotIndex, (int)wifi.txCurrentSlot, io.TXSTAT.value);
			}
		}
	}

	wifiHandler->CommTrigger();
}

/*******************************************************************************

	Ad-hoc communication interface

 *******************************************************************************/
#define ADHOC_MAGIC					"NDSWIFI\0"
#define ADHOC_PROTOCOL_VERSION 		0x0100  // v1.0

typedef struct _Adhoc_FrameHeader
{
	char magic[8];			// "NDSWIFI\0" (null terminated string)
	u16 version;			// Ad-hoc protocol version (for example 0x0502 = v5.2)
	u16 packetLen;			// Length of the packet

} Adhoc_FrameHeader;

/*******************************************************************************

	SoftAP (fake wifi access point)

 *******************************************************************************/

// Note on the CRC32 field in received packets:
// The wifi hardware doesn't store the CRC32 in memory when receiving a packet
// so the RX header length field is indeed header+body
// Hence the CRC32 has been removed from those templates.

// If you wanna change SoftAP's MAC address, change this
// Warning, don't mistake this for an array, it isn't
#define SOFTAP_MACADDR 0x00, 0xF0, 0x1A, 0x2B, 0x3C, 0x4D

const u8 SoftAP_MACAddr[6] = {SOFTAP_MACADDR};

const u8 SoftAP_Beacon[] = {
	/* 802.11 header */
	0x80, 0x00,											// Frame control
	0x00, 0x00,											// Duration ID
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,					// Receiver
	SOFTAP_MACADDR,										// Sender
	SOFTAP_MACADDR,										// BSSID
	0x00, 0x00,											// Sequence control

	/* Frame body */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,		// Timestamp (modified later)
	0x80, 0x00,											// Beacon interval
	0x21, 0x00,											// Capablilty information
	0x01, 0x02, 0x82, 0x84,								// Supported rates
	0x03, 0x01, 0x06,									// Current channel
	0x05, 0x04, 0x02, 0x01, 0x00, 0x00,					// TIM (no idea what the hell it is)
	0x00, 0x06, 'S', 'o', 'f', 't', 'A', 'P',			// SSID
};

const u8 SoftAP_ProbeResponse[] = {
	/* 802.11 header */
	0x50, 0x00,											// Frame control
	0x00, 0x00,											// Duration ID
	0x00, 0x09, 0xBF, 0x12, 0x34, 0x56,					// Receiver
	SOFTAP_MACADDR,										// Sender
	SOFTAP_MACADDR,										// BSSID
	0x00, 0x00,											// Sequence control

	/* Frame body */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,		// Timestamp (modified later)
	0x80, 0x00,											// Beacon interval
	0x21, 0x00,											// Capablilty information
	0x01, 0x02, 0x82, 0x84,								// Supported rates
	0x03, 0x01, 0x06,									// Current channel
	0x00, 0x06, 'S', 'o', 'f', 't', 'A', 'P',			// SSID
};


const u8 SoftAP_AuthFrame[] = {
	/* 802.11 header */
	0xB0, 0x00,											// Frame control
	0x00, 0x00,											// Duration ID
	0x00, 0x09, 0xBF, 0x12, 0x34, 0x56,					// Receiver
	SOFTAP_MACADDR,										// Sender
	SOFTAP_MACADDR,										// BSSID
	0x00, 0x00,											// Sequence control

	/* Frame body */
	0x00, 0x00,											// Authentication algorithm
	0x02, 0x00,											// Authentication sequence
	0x00, 0x00,											// Status
};

const u8 SoftAP_AssocResponse[] = {
	/* 802.11 header */
	0x10, 0x00,											// Frame control
	0x00, 0x00,											// Duration ID
	0x00, 0x09, 0xBF, 0x12, 0x34, 0x56,					// Receiver
	SOFTAP_MACADDR,										// Sender
	SOFTAP_MACADDR,										// BSSID
	0x00, 0x00,											// Sequence control

	/* Frame body */
	0x21, 0x00,											// Capability information
	0x00, 0x00,											// Status
	0x01, 0xC0,											// Assocation ID
	0x01, 0x02, 0x82, 0x84,								// Supported rates
};

// Deauthentication frame - sent if the user chose not to connect to WFC
const u8 SoftAP_DeauthFrame[] = {
	/* 802.11 header */
	0xC0, 0x00,											// Frame control
	0x00, 0x00,											// Duration ID
	0x00, 0x09, 0xBF, 0x12, 0x34, 0x56,					// Receiver
	SOFTAP_MACADDR,										// Sender
	SOFTAP_MACADDR,										// BSSID
	0x00, 0x00,											// Sequence control

	/* Frame body */
	0x01, 0x00,											// Reason code (is "unspecified" ok?)
};

static void SoftAP_PacketRX_Callback(u_char *userData, const pcap_pkthdr *pktHeader, const u_char *pktData)
{
	if (userData == NULL)
	{
		return;
	}
	
	SoftAPCommInterface *softAP = (SoftAPCommInterface *)userData;
	softAP->PacketRX(pktHeader, (u8 *)pktData);
}

void DummyPCapInterface::__CopyErrorString(char *errbuf)
{
	const char *errString = "libpcap is not available";
	strncpy(errbuf, errString, PCAP_ERRBUF_SIZE);
}

int DummyPCapInterface::findalldevs(void **alldevs, char *errbuf)
{
	this->__CopyErrorString(errbuf);
	return -1;
}

void DummyPCapInterface::freealldevs(void *alldevs)
{
	// Do nothing.
}

void* DummyPCapInterface::open(const char *source, int snaplen, int flags, int readtimeout, char *errbuf)
{
	this->__CopyErrorString(errbuf);
	return NULL;
}

void DummyPCapInterface::close(void *dev)
{
	// Do nothing.
}

int DummyPCapInterface::setnonblock(void *dev, int nonblock, char *errbuf)
{
	this->__CopyErrorString(errbuf);
	return -1;
}

int DummyPCapInterface::sendpacket(void *dev, const void *data, int len)
{
	return -1;
}

int DummyPCapInterface::dispatch(void *dev, int num, void *callback, void *userdata)
{
	return -1;
}

#ifndef HOST_WINDOWS

int POSIXPCapInterface::findalldevs(void **alldevs, char *errbuf)
{
	return pcap_findalldevs((pcap_if_t **)alldevs, errbuf);
}

void POSIXPCapInterface::freealldevs(void *alldevs)
{
	pcap_freealldevs((pcap_if_t *)alldevs);
}

void* POSIXPCapInterface::open(const char *source, int snaplen, int flags, int readtimeout, char *errbuf)
{
	return pcap_open_live(source, snaplen, flags, readtimeout, errbuf);
}

void POSIXPCapInterface::close(void *dev)
{
	pcap_close((pcap_t *)dev);
}

int POSIXPCapInterface::setnonblock(void *dev, int nonblock, char *errbuf)
{
	return pcap_setnonblock((pcap_t *)dev, nonblock, errbuf);
}

int POSIXPCapInterface::sendpacket(void *dev, const void *data, int len)
{
	return pcap_sendpacket((pcap_t *)dev, (u_char *)data, len);
}

int POSIXPCapInterface::dispatch(void *dev, int num, void *callback, void *userdata)
{
	if (callback == NULL)
	{
		return -1;
	}
	
	return pcap_dispatch((pcap_t *)dev, num, (pcap_handler)callback, (u_char *)userdata);
}

#endif

WifiCommInterface::WifiCommInterface()
{
	_usecCounter = 0;
	_emulationLevel = WifiEmulationLevel_Off;
}

WifiCommInterface::~WifiCommInterface()
{
	// Do nothing... for now.
}

AdhocCommInterface::AdhocCommInterface()
{
	_commInterfaceID = WifiCommInterfaceID_AdHoc;

	_wifiSocket = (socket_t *)malloc(sizeof(socket_t));
	*((socket_t *)_wifiSocket) = INVALID_SOCKET;

	_sendAddr = (sockaddr_t *)malloc(sizeof(sockaddr_t));

	_packetBuffer = NULL;
}

AdhocCommInterface::~AdhocCommInterface()
{
	this->Stop();

	free(this->_wifiSocket);
	free(this->_sendAddr);
}

bool AdhocCommInterface::Start(WifiEmulationLevel emulationLevel)
{
	int socketOptValueTrue = 1;
	int result = -1;
	
	this->_usecCounter = 0;
	
	// Ad-hoc mode won't have a partial-functioning variant, and so just return
	// if the WiFi emulation level is Off.
	if (emulationLevel == WifiEmulationLevel_Off)
	{
		this->_emulationLevel = WifiEmulationLevel_Off;
		return false;
	}

	socket_t &thisSocket = *((socket_t *)this->_wifiSocket);
	
	// Create an UDP socket.
	thisSocket = socket(AF_INET, SOCK_DGRAM, 0);
	if (thisSocket < 0)
	{
		thisSocket = INVALID_SOCKET;
		this->_emulationLevel = WifiEmulationLevel_Off;
		
		// Ad-hoc mode really needs a socket to work at all, so don't even bother
		// running this comm interface if we didn't get a working socket.
		WIFI_LOG(1, "Ad-hoc: Failed to create socket.\n");
		return false;
	}
	
	// Enable the socket to be bound to an address/port that is already in use
	// This enables us to communicate with another DeSmuME instance running on the same computer.
	result = setsockopt(thisSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&socketOptValueTrue, sizeof(int));
	
#ifdef SO_REUSEPORT
	// Some platforms also need to enable SO_REUSEPORT, so do so if its necessary.
	result = setsockopt(thisSocket, SOL_SOCKET, SO_REUSEPORT, (const char *)&socketOptValueTrue, sizeof(int));
#endif
	
	// Bind the socket to any address on port 7000.
	sockaddr_t saddr;
	saddr.sa_family = AF_INET;
	*(u32 *)&saddr.sa_data[2] = htonl(INADDR_ANY);
	*(u16 *)&saddr.sa_data[0] = htons(BASEPORT);
	
	result = bind(thisSocket, &saddr, sizeof(sockaddr_t));
	if (result < 0)
	{
		closesocket(thisSocket);
		thisSocket = INVALID_SOCKET;
		this->_emulationLevel = WifiEmulationLevel_Off;
		
		WIFI_LOG(1, "Ad-hoc: Failed to bind the socket.\n");
		return false;
	}
	
	// Enable broadcast mode
	// Not doing so results in failure when sendto'ing to broadcast address
	result = setsockopt(thisSocket, SOL_SOCKET, SO_BROADCAST, (const char *)&socketOptValueTrue, sizeof(int));
	if (result < 0)
	{
		closesocket(thisSocket);
		thisSocket = INVALID_SOCKET;
		this->_emulationLevel = WifiEmulationLevel_Off;
		
		WIFI_LOG(1, "Ad-hoc: Failed to enable broadcast mode.\n");
		return false;
	}
	
	// Prepare an address structure for sending packets.
	sockaddr_t &thisSendAddr = *((sockaddr_t *)this->_sendAddr);

	thisSendAddr.sa_family = AF_INET;
	*(u32 *)&thisSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
	*(u16 *)&thisSendAddr.sa_data[0] = htons(BASEPORT);
	
	// Allocate 16KB worth of memory for sending packets. Hopefully, this should be plenty.
	this->_packetBuffer = (u8 *)malloc(16 * 1024);
	
	this->_emulationLevel = emulationLevel;
	WIFI_LOG(1, "Ad-hoc: Initialization successful.\n");
	return true;
}

void AdhocCommInterface::Stop()
{
	socket_t &thisSocket = *((socket_t *)this->_wifiSocket);

	if (thisSocket >= 0)
	{
		closesocket(thisSocket);
	}
	
	free(this->_packetBuffer);
	this->_packetBuffer = NULL;
}

void AdhocCommInterface::SendPacket(void *data, size_t len)
{
	socket_t &thisSocket = *((socket_t *)this->_wifiSocket);
	sockaddr_t &thisSendAddr = *((sockaddr_t *)this->_sendAddr);

	if (thisSocket < 0)
	{
		return;
	}
	
	WIFI_LOG(2, "Ad-hoc: sending a packet of %i bytes, frame control: %04X\n", (int)len, *(u16 *)data);
	
	size_t frameLen = sizeof(Adhoc_FrameHeader) + len;
	u8 *ptr = this->_packetBuffer;
	
	Adhoc_FrameHeader header;
	strncpy(header.magic, ADHOC_MAGIC, 8);
	header.version = ADHOC_PROTOCOL_VERSION;
	header.packetLen = len;
	memcpy(ptr, &header, sizeof(Adhoc_FrameHeader));
	ptr += sizeof(Adhoc_FrameHeader);
	
	memcpy(ptr, data, len);
	
	size_t nbytes = sendto(thisSocket, (const char *)this->_packetBuffer, frameLen, 0, &thisSendAddr, sizeof(sockaddr_t));
	
	WIFI_LOG(4, "Ad-hoc: sent %i/%i bytes of packet.\n", (int)nbytes, (int)frameLen);
}

void AdhocCommInterface::Trigger()
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	socket_t &thisSocket = *((socket_t *)this->_wifiSocket);

	this->_usecCounter++;

	if (thisSocket < 0)
	{
		return;
	}
	
	// Check every millisecond if we received a packet
	if (!(this->_usecCounter & 1023))
	{
		fd_set fd;
		struct timeval tv;

		FD_ZERO(&fd);
		FD_SET(thisSocket, &fd);
		tv.tv_sec = 0;
		tv.tv_usec = 0;
 
		if (select(thisSocket+1, &fd, NULL, NULL, &tv))
		{
			sockaddr_t fromAddr;
			socklen_t fromLen = sizeof(sockaddr_t);
			u8 buf[1536];
			u8 *ptr;
			u16 packetLen;

			int nbytes = recvfrom(thisSocket, (char *)buf, 1536, 0, &fromAddr, &fromLen);

			// No packet arrived (or there was an error)
			if (nbytes <= 0)
				return;

			ptr = buf;
			Adhoc_FrameHeader header = *(Adhoc_FrameHeader *)ptr;
			
			// Check the magic string in header
			if (strncmp(header.magic, ADHOC_MAGIC, 8))
				return;

			// Check the ad-hoc protocol version
			if (header.version != ADHOC_PROTOCOL_VERSION)
				return;

			packetLen = header.packetLen;
			ptr += sizeof(Adhoc_FrameHeader);

			// If the packet is for us, send it to the wifi core
			if (!WIFI_compareMAC(&ptr[10], io.MACADDR))
			{
				if (WIFI_isBroadcastMAC(&ptr[16]) ||
					WIFI_compareMAC(&ptr[16], io.BSSID) ||
					WIFI_isBroadcastMAC(io.BSSID))
				{
				/*	printf("packet was for us: mac=%02X:%02X.%02X.%02X.%02X.%02X, bssid=%02X:%02X.%02X.%02X.%02X.%02X\n",
						wifiMac.mac.bytes[0], wifiMac.mac.bytes[1], wifiMac.mac.bytes[2], wifiMac.mac.bytes[3], wifiMac.mac.bytes[4], wifiMac.mac.bytes[5],
						wifiMac.bss.bytes[0], wifiMac.bss.bytes[1], wifiMac.bss.bytes[2], wifiMac.bss.bytes[3], wifiMac.bss.bytes[4], wifiMac.bss.bytes[5]);
					printf("da=%02X:%02X.%02X.%02X.%02X.%02X, sa=%02X:%02X.%02X.%02X.%02X.%02X, bssid=%02X:%02X.%02X.%02X.%02X.%02X\n",
						ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9],
						ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15],
						ptr[16], ptr[17], ptr[18], ptr[19], ptr[20], ptr[21]);*/
				/*	WIFI_LOG(3, "Ad-hoc: received a packet of %i bytes from %i.%i.%i.%i (port %i).\n",
						nbytes,
						(u8)fromAddr.sa_data[2], (u8)fromAddr.sa_data[3],
						(u8)fromAddr.sa_data[4], (u8)fromAddr.sa_data[5],
						ntohs(*(u16*)&fromAddr.sa_data[0]));*/
					WIFI_LOG(2, "Ad-hoc: received a packet of %i bytes, frame control: %04X\n", packetLen, *(u16*)&ptr[0]);
					//WIFI_LOG(2, "Storing packet at %08X.\n", 0x04804000 + (wifiMac.RXWriteCursor<<1));

					//if (((*(u16*)&ptr[0]) != 0x0080) && ((*(u16*)&ptr[0]) != 0x0228))
					//	printf("received packet, framectl=%04X\n", (*(u16*)&ptr[0]));

					//if ((*(u16*)&ptr[0]) == 0x0228)
					//	printf("wifi: received fucking packet!\n");

					WIFI_triggerIRQ(WIFI_IRQ_RXSTART);

					u8 *packet = new u8[12 + packetLen];

					WIFI_MakeRXHeader(packet, WIFI_GetRXFlags(ptr), 20, packetLen, 0, 0);
					memcpy(&packet[12], ptr, packetLen);

					for (int i = 0; i < (12 + packetLen); i += 2)
					{
						u16 word = *(u16 *)&packet[i];
						WIFI_RXPutWord(word);
					}

					io.RXBUF_WRCSR.HalfwordAddress = ((io.RXBUF_WRCSR.HalfwordAddress + 1) & (~1));
					
					//wifiMac.RXNum++;
					WIFI_triggerIRQ(WIFI_IRQ_RXEND);
					
					delete [] packet;
				}
			}
		}
	}
}

SoftAPCommInterface::SoftAPCommInterface()
{
	_commInterfaceID = WifiCommInterfaceID_Infrastructure;
	_pcap = &dummyPCapInterface;
	_bridgeDeviceIndex = 0;
	_bridgeDevice = NULL;
	_packetCaptureFile = NULL;
	
	memset(_curPacket, 0, sizeof(_curPacket));
	_curPacketSize = 0;
	_curPacketPos = 0;
	_curPacketSending = false;
	_status = APStatus_Disconnected;
	_seqNum = 0;
}

SoftAPCommInterface::~SoftAPCommInterface()
{
	this->Stop();
}

void* SoftAPCommInterface::_GetBridgeDeviceAtIndex(int deviceIndex, char *outErrorBuf)
{
	void *deviceList = NULL;
	void *theDevice = NULL;
	int result = this->_pcap->findalldevs((void **)&deviceList, outErrorBuf);
	
	if ( (result == -1) || (deviceList == NULL) )
	{
		WIFI_LOG(1, "SoftAP: Failed to find any network adapter: %s\n", outErrorBuf);
		return theDevice;
	}
	
	pcap_if_t *currentDevice = (pcap_if_t *)deviceList;
	
	for (int i = 0; i < deviceIndex; i++)
	{
		currentDevice = currentDevice->next;
	}
	
	theDevice = this->_pcap->open(currentDevice->name, PACKET_SIZE, PCAP_OPENFLAG_PROMISCUOUS, 1, outErrorBuf);
	if (theDevice == NULL)
	{
		WIFI_LOG(1, "SoftAP: Failed to open device %s: %s\n", currentDevice->PCAP_DEVICE_NAME, outErrorBuf);
	}
	else
	{
		WIFI_LOG(1, "SoftAP: Device %s successfully opened.\n", currentDevice->PCAP_DEVICE_NAME);
	}
	
	this->_pcap->freealldevs(deviceList);
	
	return theDevice;
}

bool SoftAPCommInterface::_IsDNSRequestToWFC(u16 ethertype, u8 *body)
{
	// Check the various headers...
	if (ntohs(ethertype) != 0x0800) return false;		// EtherType: IP
	if (body[0] != 0x45) return false;					// Version: 4, header len: 5
	if (body[9] != 0x11) return false;					// Protocol: UDP
	if (ntohs(*(u16*)&body[22]) != 53) return false;	// Dest. port: 53 (DNS)
	if (htons(ntohs(*(u16*)&body[28+2])) & 0x8000) return false;	// must be a query
	
	// Analyze each question
	u16 numquestions = ntohs(*(u16*)&body[28+4]);
	u32 curoffset = 28+12;
	for (u16 curquestion = 0; curquestion < numquestions; curquestion++)
	{
		// Assemble the requested domain name
		u8 bitlength = 0; char domainname[256] = "";
		while ((bitlength = body[curoffset++]) != 0)
		{
			strncat(domainname, (const char *)&body[curoffset], bitlength);
			
			curoffset += bitlength;
			if (body[curoffset] != 0)
			{
				strcat(domainname, ".");
			}
		}
		
		// if the domain name contains nintendowifi.net
		// it is most likely a WFC server
		// (note, conntest.nintendowifi.net just contains a dummy HTML page and
		// is used for connection tests, I think we can let this one slide)
		if ((strstr(domainname, "nintendowifi.net") != NULL) &&
			(strcmp(domainname, "conntest.nintendowifi.net") != 0))
		{
			return true;
		}
		
		// Skip the type and class - we don't care about that
		curoffset += 4;
	}
	
	return false;
}

void SoftAPCommInterface::_Deauthenticate()
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	u32 packetLen = sizeof(SoftAP_DeauthFrame);
	
	memcpy(&this->_curPacket[12], SoftAP_DeauthFrame, packetLen);
	
	memcpy(&this->_curPacket[12 + 4], FW_Mac, 6); // Receiver MAC
	
	*(u16 *)&this->_curPacket[12 + 22] = this->_seqNum << 4;		// Sequence number
	this->_seqNum++;
	
	u16 rxflags = 0x0010;
	if (WIFI_compareMAC(io.BSSID, &this->_curPacket[12 + 16]))
	{
		rxflags |= 0x8000;
	}
	
	WIFI_MakeRXHeader(this->_curPacket, rxflags, 20, packetLen, 0, 0);
	
	// Let's prepare to send
	this->_curPacketSize = packetLen + 12;
	this->_curPacketPos = 0;
	this->_curPacketSending = true;
	
	this->_status = APStatus_Disconnected;
}

void SoftAPCommInterface::_SendBeacon()
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	u32 packetLen = sizeof(SoftAP_Beacon);
	
	memcpy(&this->_curPacket[12], SoftAP_Beacon, packetLen);	// Copy the beacon template
	
	*(u16 *)&this->_curPacket[12 + 22] = this->_seqNum << 4;	// Sequence number
	this->_seqNum++;
	
	u64 timestamp = this->_usecCounter;
	*(u64 *)&this->_curPacket[12 + 24] = timestamp;				// Timestamp
	
	u16 rxflags = 0x0011;
	if (WIFI_compareMAC(io.BSSID, &this->_curPacket[12 + 16]))
		rxflags |= 0x8000;
	
	WIFI_MakeRXHeader(this->_curPacket, rxflags, 20, packetLen, 0, 0);
	
	// Let's prepare to send
	this->_curPacketSize = packetLen + 12;
	this->_curPacketPos = 0;
	this->_curPacketSending = true;
}

void SoftAPCommInterface::PacketRX(const void *pktHeader, const u8 *pktData)
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	
	// safety checks
	if ( (pktData == NULL) || (pktHeader == NULL) )
	{
		return;
	}
	
	// reject the packet if it wasn't for us
	if (!(WIFI_isBroadcastMAC(&pktData[0]) || WIFI_compareMAC(&pktData[0], io.MACADDR)))
	{
		return;
	}
	
	// reject the packet if we just sent it
	if (WIFI_compareMAC(&pktData[6], io.MACADDR))
	{
		return;
	}
	
	pcap_pkthdr *header = (pcap_pkthdr *)pktHeader;
	u32 headerLength = header->len;
	
	if (this->_curPacketSending)
	{
		printf("crap we're gonna nuke a packet at %i/%i (%04X) (%04X)\n", this->_curPacketPos, this->_curPacketSize, *(u16 *)&this->_curPacket[12], io.RXBUF_WRCSR.HalfwordAddress<<1);
	}
	
	// The packet was for us. Let's process it then.
	WIFI_triggerIRQ(WIFI_IRQ_RXSTART);
	
	int wpacketLen = WIFI_alignedLen(26 + 6 + (headerLength-14));
	u8 wpacket[2048];
	
	// Save ethernet packet into the PCAP file.
	// Filter broadcast because of privacy. They aren't needed to study the protocol with the nintendo server
	// and can include PC Discovery protocols
#if WIFI_SAVE_PCAP_TO_FILE
	if (!WIFI_isBroadcastMAC(&pktData[0]))
	{
		this->PacketCaptureFileWrite(pktData, headerLength, true);
	}
#endif
	//printf("RECEIVED DATA FRAME: len=%i/%i, src=%02X:%02X:%02X:%02X:%02X:%02X, dst=%02X:%02X:%02X:%02X:%02X:%02X, ethertype=%04X\n",
	//	24+ (h->caplen-12), 24 + (h->len-12), data[6], data[7], data[8], data[9], data[10], data[11],
	//	data[0], data[1], data[2], data[3], data[4], data[5], *(u16*)&data[12]);
	
	u16 rxflags = 0x0018;
	if (WIFI_compareMAC(io.BSSID, (u8 *)SoftAP_MACAddr))
	{
		rxflags |= 0x8000;
	}
	
	// Make a valid 802.11 frame
	WIFI_MakeRXHeader(wpacket, rxflags, 20, wpacketLen, 0, 0);
	*(u16*)&wpacket[12+0] = 0x0208;
	*(u16*)&wpacket[12+2] = 0x0000;
	memcpy(&wpacket[12+4], &pktData[0], 6);
	memcpy(&wpacket[12+10], SoftAP_MACAddr, 6);
	memcpy(&wpacket[12+16], &pktData[6], 6);
	*(u16*)&wpacket[12+22] = this->_seqNum << 4;
	*(u16*)&wpacket[12+24] = 0xAAAA;
	*(u16*)&wpacket[12+26] = 0x0003;
	*(u16*)&wpacket[12+28] = 0x0000;
	*(u16*)&wpacket[12+30] = *(u16 *)&pktData[12];
	memcpy(&wpacket[12+32], &pktData[14], headerLength-14);
	
	this->_seqNum++;
	
	// put it in the RX buffer
	for (int i = 0; i < (12 + wpacketLen); i += 2)
	{
		u16 word = *(u16 *)&wpacket[i];
		WIFI_RXPutWord(word);
	}
	
	// Done!
	io.RXBUF_WRCSR.HalfwordAddress = ((io.RXBUF_WRCSR.HalfwordAddress + 1) & (~1));
	
	WIFI_triggerIRQ(WIFI_IRQ_RXEND);
}

// Create and open a PCAP file to store different Ethernet packets
// of the current connection.
void SoftAPCommInterface::PacketCaptureFileOpen()
{
	// Create file using as name the current time and game code
	time_t ti;
	time(&ti);
	tm* t = localtime(&ti);
	
	char* gamecd = gameInfo.header.gameCode;
	char file_name[50];
	sprintf(
			file_name,
			"%c%c%c%c [%02d-%02d-%02d-%02d].pcap",
			gamecd[0], gamecd[1], gamecd[2], gamecd[3],
			t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec
			);
	
	// Open as binary write
	this->_packetCaptureFile = fopen(file_name, "wb");
	if (this->_packetCaptureFile == NULL)
	{
		printf("Can't create capture log file: %s\n", file_name);
	}
	else
	{
		// Create global header of LCAP packet
		// More info here: http://www.kroosec.com/2012/10/a-look-at-pcap-file-format.html
		const u32 magic_header  = 0xa1b2c3d4;
		const u16 major_version = 0x02;
		const u16 minor_version = 0x04;
		const u32 gmt_time      = 0x00000000; // usually not used
		const u32 pre_time      = 0x00000000; // usually not used
		const u32 snapshot_len  = 0x0000ffff; // Maximum length of each packet
		const u32 ll_header_typ = 0x00000001; // For Ethernet
		
		fwrite(&magic_header,  sizeof(char), 4, this->_packetCaptureFile);
		fwrite(&major_version, sizeof(char), 2, this->_packetCaptureFile);
		fwrite(&minor_version, sizeof(char), 2, this->_packetCaptureFile);
		fwrite(&gmt_time,      sizeof(char), 4, this->_packetCaptureFile);
		fwrite(&pre_time,      sizeof(char), 4, this->_packetCaptureFile);
		fwrite(&snapshot_len,  sizeof(char), 4, this->_packetCaptureFile);
		fwrite(&ll_header_typ, sizeof(char), 4, this->_packetCaptureFile);
		
		fflush(this->_packetCaptureFile);
	}
}

void SoftAPCommInterface::PacketCaptureFileClose()
{
	if (this->_packetCaptureFile != NULL)
	{
		fclose(this->_packetCaptureFile);
		this->_packetCaptureFile = NULL;
	}
}

// Save an Ethernet packet into the PCAP file of the current connection.
void SoftAPCommInterface::PacketCaptureFileWrite(const u8 *packet, u32 len, bool isReceived)
{
	if (this->_packetCaptureFile == NULL)
	{
		printf("Can't save packet... %d\n", isReceived);
		return;
	}
	
	const u32 seconds = this->_usecCounter / 1000000;
	const u32 millis = this->_usecCounter % 1000000;
	
	// Add the packet
	// more info: http://www.kroosec.com/2012/10/a-look-at-pcap-file-format.html
	printf("WIFI: Saving packet of %04x bytes | %d\n", len, isReceived);
	
	// First create the header
	fwrite(&seconds, sizeof(char), 4, this->_packetCaptureFile); // This should be seconds since Unix Epoch, but it works :D
	fwrite(&millis,  sizeof(char), 4, this->_packetCaptureFile);
	fwrite(&len,     sizeof(char), 4, this->_packetCaptureFile);
	fwrite(&len,     sizeof(char), 4, this->_packetCaptureFile);
	
	// Then write the packet
	fwrite(packet, sizeof(char), len, this->_packetCaptureFile);
	
	// Flush the file
	fflush(this->_packetCaptureFile);
}

void SoftAPCommInterface::SetPCapInterface(ClientPCapInterface *pcapInterface)
{
	this->_pcap = (pcapInterface == NULL) ? &dummyPCapInterface : pcapInterface;
}

ClientPCapInterface* SoftAPCommInterface::GetPCapInterface()
{
	return this->_pcap;
}

int SoftAPCommInterface::GetBridgeDeviceIndex()
{
	return this->_bridgeDeviceIndex;
}

void SoftAPCommInterface::SetBridgeDeviceIndex(int deviceIndex)
{
	this->_bridgeDeviceIndex = deviceIndex;
}

bool SoftAPCommInterface::Start(WifiEmulationLevel emulationLevel)
{
	const bool isPCapSupported = (this->_pcap != &dummyPCapInterface);
	WifiEmulationLevel pendingEmulationLevel = emulationLevel;
	char errbuf[PCAP_ERRBUF_SIZE];
	
	this->_usecCounter = 0;
	
	memset(this->_curPacket, 0, sizeof(this->_curPacket));
	this->_curPacketSize = 0;
	this->_curPacketPos = 0;
	this->_curPacketSending = false;
	
	this->_status = APStatus_Disconnected;
	this->_seqNum = 0;
	
	if (isPCapSupported && (emulationLevel != WifiEmulationLevel_Off))
	{
		this->_bridgeDevice = this->_GetBridgeDeviceAtIndex(this->_bridgeDeviceIndex, errbuf);
	}
	else
	{
		this->_bridgeDevice = NULL;
		pendingEmulationLevel = WifiEmulationLevel_Off;
		
		if (!isPCapSupported)
		{
			WIFI_LOG(1, "SoftAP: No libpcap interface has been set.\n");
		}
		
		if (emulationLevel == WifiEmulationLevel_Off)
		{
			WIFI_LOG(1, "SoftAP: Emulation level is OFF.\n");
		}
	}
	
	// Set non-blocking mode
	if (this->_bridgeDevice != NULL)
	{
		int result = this->_pcap->setnonblock(this->_bridgeDevice, 1, errbuf);
		if (result == -1)
		{
			this->_pcap->close(this->_bridgeDevice);
			this->_bridgeDevice = NULL;
			pendingEmulationLevel = WifiEmulationLevel_Off;
			
			WIFI_LOG(1, "SoftAP: libpcap failed to set non-blocking mode: %s\n", errbuf);
		}
		else
		{
			WIFI_LOG(1, "SoftAP: libpcap has set non-blocking mode.\n");
		}
	}
	
	this->_emulationLevel = pendingEmulationLevel;
	return true;
}

void SoftAPCommInterface::Stop()
{
	this->PacketCaptureFileClose();
	
	if (this->_bridgeDevice != NULL)
	{
		this->_pcap->close(this->_bridgeDevice);
		this->_bridgeDevice = NULL;
	}
}

void SoftAPCommInterface::SendPacket(void *data, size_t len)
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	u8 *packet = (u8 *)data;
	u16 frameCtl = *(u16 *)&packet[0];

	WIFI_LOG(3, "SoftAP: Received a packet of length %i bytes. Frame control = %04X\n",
		(int)len, frameCtl);

	//use this to log wifi messages easily
	/*static int ctr=0;
	char buf[100];
	sprintf(buf,"wifi%04d.txt",ctr);
	FILE* outf = fopen(buf,"wb");
	fwrite(packet,1,len,outf);
	fclose(outf);
	ctr++;*/

	switch((frameCtl >> 2) & 0x3)
	{
		case 0x0:				// Management frame
		{
			u32 packetLen;

			switch ((frameCtl >> 4) & 0xF)
			{
				case 0x4:		// Probe request (WFC)
				{
					packetLen = sizeof(SoftAP_ProbeResponse);
					memcpy(&this->_curPacket[12], SoftAP_ProbeResponse, packetLen);

					// Add the timestamp
					u64 timestamp = this->_usecCounter;
					*(u64 *)&this->_curPacket[12 + 24] = timestamp;
					
					break;
				}

				case 0xB:		// Authentication
				{
					packetLen = sizeof(SoftAP_AuthFrame);
					memcpy(&this->_curPacket[12], SoftAP_AuthFrame, packetLen);
					this->_status = APStatus_Authenticated;
					
					break;
				}

				case 0x0:		// Association request
				{
					if (this->_status != APStatus_Authenticated)
					{
						return;
					}
					
					packetLen = sizeof(SoftAP_AssocResponse);
					memcpy(&this->_curPacket[12], SoftAP_AssocResponse, packetLen);

					this->_status = APStatus_Associated;
					WIFI_LOG(1, "SoftAP connected!\n");
#if WIFI_SAVE_PCAP_TO_FILE
					this->PacketCaptureFileOpen();
#endif
					break;
				}

				case 0xA:		// Disassociation
					this->_status = APStatus_Authenticated;
					return;

				case 0xC:		// Deauthentication
				{
					this->_status = APStatus_Disconnected;
					WIFI_LOG(1, "SoftAP disconnected.\n");
					this->PacketCaptureFileClose();
					return;
				}
					
				default:
					WIFI_LOG(2, "SoftAP: unknown management frame type %04X\n", (frameCtl >> 4) & 0xF);
					return;
			}

			memcpy(&this->_curPacket[12 + 4], FW_Mac, 6); // Receiver MAC

			*(u16 *)&this->_curPacket[12 + 22] = this->_seqNum << 4; // Sequence number
			this->_seqNum++;

			u16 rxflags = 0x0010;
			if (WIFI_compareMAC(io.BSSID, &this->_curPacket[12 + 16]))
			{
				rxflags |= 0x8000;
			}
			
			WIFI_MakeRXHeader(this->_curPacket, rxflags, 20, packetLen, 0, 0); // make the RX header

			// Let's prepare to send
			this->_curPacketSize = packetLen + 12;
			this->_curPacketPos = 0;
			this->_curPacketSending = true;
		}
		break;

		case 0x2:				// Data frame
		{
			// If it has a LLC/SLIP header, send it over the Ethernet
			if ((*(u16 *)&packet[24] == 0xAAAA) && (*(u16 *)&packet[26] == 0x0003) && (*(u16 *)&packet[28] == 0x0000))
			{
				if (this->_status != APStatus_Associated)
					return;

				if (this->_IsDNSRequestToWFC(*(u16 *)&packet[30], &packet[32]))
				{
					// Removed to allow WFC communication.
					//SoftAP_Deauthenticate();
					//return;
				}

				u32 epacketLen = ((len - 30 - 4) + 14);
				u8 epacket[2048];

				//printf("----- SENDING ETHERNET PACKET: len=%i, ethertype=%04X -----\n",
				//	len, *(u16*)&packet[30]);

				memcpy(&epacket[0], &packet[16], 6);
				memcpy(&epacket[6], &packet[10], 6);
				*(u16 *)&epacket[12] = *(u16 *)&packet[30];
				memcpy(&epacket[14], &packet[32], epacketLen - 14);

				if (this->_bridgeDevice != NULL)
				{
#if WIFI_SAVE_PCAP_TO_FILE
					// Store the packet in the PCAP file.
					this->PacketCaptureFileWrite(epacket, epacketLen, false);
#endif
					// Send it
					this->_pcap->sendpacket(this->_bridgeDevice, epacket, epacketLen);
				}
			}
			else
			{
				WIFI_LOG(1, "SoftAP: received non-Ethernet data frame. wtf?\n");
			}
		}
		break;
	}
}

void SoftAPCommInterface::Trigger()
{
	WIFI_IOREG_MAP &io = wifiHandler->GetWifiData().io;
	
	this->_usecCounter++;

	// other packets will have priority over beacons
	// 'cause they might be only once of them
	// whereas there will be sooo much beacons
	if (!this->_curPacketSending) // && SoftAP.status != APStatus_Associated)
	{
		// if(wifiMac.ioMem[0xD0 >> 1] & 0x0400)
		{
			//zero sez: every 1/10 second? does it have to be precise? this is so costly..
			// Okay for 128 ms then
			if ((this->_usecCounter & 131071) == 0)
			{
				this->_SendBeacon();
			}
		}
	}

	/* Given a connection of 2 megabits per second, */
	/* we take ~4 microseconds to transfer a byte, */
	/* ie ~8 microseconds to transfer a word. */
	if ((this->_curPacketSending) && !(this->_usecCounter & 7))
	{
		if (this->_curPacketPos == 0)
		{
			WIFI_triggerIRQ(WIFI_IRQ_RXSTART);
		}
		
		u16 word = *(u16 *)&this->_curPacket[this->_curPacketPos];
		WIFI_RXPutWord(word);

		this->_curPacketPos += 2;
		if (this->_curPacketPos >= this->_curPacketSize)
		{
			this->_curPacketSize = 0;
			this->_curPacketPos = 0;
			this->_curPacketSending = false;

			io.RXBUF_WRCSR.HalfwordAddress = ((io.RXBUF_WRCSR.HalfwordAddress + 1) & (~1));
			
			WIFI_triggerIRQ(WIFI_IRQ_RXEND);
		}
	}

	// EXTREMELY EXPERIMENTAL packet receiving code
	// Can now receive 64 packets per millisecond. Completely arbitrary limit. Todo: tweak if needed.
	// But due to using non-blocking mode, this shouldn't be as slow as it used to be.
	if (this->_bridgeDevice != NULL)
	{
		if ((this->_usecCounter & 1023) == 0)
		{
			this->_pcap->dispatch(this->_bridgeDevice, 64, (void *)&SoftAP_PacketRX_Callback, this);
		}
	}
}

WifiHandler::WifiHandler()
{
	_selectedEmulationLevel = WifiEmulationLevel_Off;
	_currentEmulationLevel = _selectedEmulationLevel;
	
	_adhocCommInterface = new AdhocCommInterface;
	_softAPCommInterface = new SoftAPCommInterface;
	
	_selectedCommID = WifiCommInterfaceID_AdHoc;
	_currentCommID = _selectedCommID;
	_currentCommInterface = NULL;
	
	_selectedBridgeDeviceIndex = 0;
	
	_adhocMACMode = WifiMACMode_Automatic;
	_infrastructureMACMode = WifiMACMode_ReadFromFirmware;
	_ip4Address = 0;
	_uniqueMACValue = 0;
	
#ifndef HOST_WINDOWS
	_pcap = new POSIXPCapInterface;
	_isSocketsSupported = true;
#else
	_pcap = &dummyPCapInterface;
	_isSocketsSupported = false;
#endif
	
	WIFI_initCRC32Table();
	Reset();
}

WifiHandler::~WifiHandler()
{
	delete this->_currentCommInterface;
}

void WifiHandler::Reset()
{
	memset(&legacyWifiSF, 0, sizeof(LegacyWifiSFormat));
	memset(&_wifi, 0, sizeof(WifiData));
	WIFI_resetRF(this->_wifi.rf);
	
	_wifi.io.POWER_US.Disable = 1;
	_wifi.io.POWERSTATE.IsPowerOff = 1;
	_wifi.io.TXREQ_READ.UNKNOWN1 = 1;
	_wifi.io.BB_POWER.Disable = 0x0D;
	_wifi.io.BB_POWER.DisableAllPorts = 1;
	_wifi.io.RF_PINS.UNKNOWN1 = 1;
	
	this->_didWarnWFCUser = false;
}

WifiData& WifiHandler::GetWifiData()
{
	return this->_wifi;
}

TXPacketInfo& WifiHandler::GetPacketInfoAtSlot(size_t txSlot)
{
	if (txSlot > 4)
	{
		return this->_wifi.txPacketInfo[0];
	}
	
	return this->_wifi.txPacketInfo[txSlot];
}

WifiEmulationLevel WifiHandler::GetSelectedEmulationLevel()
{
	return this->_selectedEmulationLevel;
}

WifiEmulationLevel WifiHandler::GetCurrentEmulationLevel()
{
	return this->_currentEmulationLevel;
}

void WifiHandler::SetEmulationLevel(WifiEmulationLevel emulationLevel)
{
#ifdef EXPERIMENTAL_WIFI_COMM
	this->_selectedEmulationLevel = emulationLevel;
#else
	this->_selectedEmulationLevel = WifiEmulationLevel_Off;
#endif
}

WifiCommInterfaceID WifiHandler::GetSelectedCommInterfaceID()
{
	return this->_selectedCommID;
}

WifiCommInterfaceID WifiHandler::GetCurrentCommInterfaceID()
{
	return this->_currentCommID;
}

void WifiHandler::SetCommInterfaceID(WifiCommInterfaceID commID)
{
	this->_selectedCommID = commID;
}

int WifiHandler::GetBridgeDeviceList(std::vector<std::string> *deviceStringList)
{
	int result = -1;

	if (deviceStringList == NULL)
	{
		return result;
	}

	char errbuf[PCAP_ERRBUF_SIZE];
	pcap_if_t *deviceList;
	
	result = this->GetPCapInterface()->findalldevs((void **)&deviceList, errbuf);
	if ( (result == -1) || (deviceList == NULL) )
	{
		return result;
	}
	
	pcap_if_t *currentDevice = deviceList;
	for (size_t i = 0; currentDevice != NULL; i++, currentDevice = currentDevice->next)
	{
		// on x64 description is empty
		if (currentDevice->description[0] == 0)
		{
			deviceStringList->push_back(currentDevice->name);
		}
		else
		{
			deviceStringList->push_back(currentDevice->description);
		}
	}
	
	return deviceStringList->size();
}

int WifiHandler::GetSelectedBridgeDeviceIndex()
{
	return this->_selectedBridgeDeviceIndex;
}

int WifiHandler::GetCurrentBridgeDeviceIndex()
{
	return this->_softAPCommInterface->GetBridgeDeviceIndex();
}

void WifiHandler::SetBridgeDeviceIndex(int deviceIndex)
{
	this->_selectedBridgeDeviceIndex = deviceIndex;
}

bool WifiHandler::CommStart()
{
	bool result = false;
	
	WifiEmulationLevel pendingEmulationLevel = this->_selectedEmulationLevel;
	WifiCommInterface *selectedCommInterface = NULL;
	WifiMACMode selectedMACMode = WifiMACMode_Manual;
	
	if (pendingEmulationLevel != WifiEmulationLevel_Off)
	{
		switch (this->_selectedCommID)
		{
			case WifiCommInterfaceID_AdHoc:
			{
				if (this->_isSocketsSupported)
				{
					selectedCommInterface = this->_adhocCommInterface;
					selectedMACMode = this->_adhocMACMode;
				}
				else
				{
					WIFI_LOG(1, "Ad-hoc mode requires sockets, but sockets are not supported on this system.\n");
					pendingEmulationLevel = WifiEmulationLevel_Off;
				}
				break;
			}
				
			case WifiCommInterfaceID_Infrastructure:
			{
				selectedCommInterface = this->_softAPCommInterface;
				selectedMACMode = this->_infrastructureMACMode;
				
				if (!this->IsPCapSupported())
				{
					WIFI_LOG(1, "Infrastructure mode requires libpcap for full functionality,\n      but libpcap is not available on this system. Network functions\n      will be disabled for this session.\n");
				}
				break;
			}
				
			default:
				break;
		}
	}
	
	// Stop the current comm interface.
	if (this->_currentCommInterface != NULL)
	{
		this->_currentCommInterface->Stop();
	}
	
	// Assign the pcap interface to SoftAP if pcap is available.
	if (this->_selectedCommID == WifiCommInterfaceID_Infrastructure)
	{
		this->_softAPCommInterface->SetPCapInterface(this->_pcap);
		this->_softAPCommInterface->SetBridgeDeviceIndex(this->_selectedBridgeDeviceIndex);
	}
	
	// Start the new comm interface.
	this->_currentCommInterface = selectedCommInterface;
	
	if (this->_currentCommInterface == NULL)
	{
		pendingEmulationLevel = WifiEmulationLevel_Off;
	}
	else
	{
		result = this->_currentCommInterface->Start(pendingEmulationLevel);
		if (!result)
		{
			this->_currentCommInterface->Stop();
			this->_currentCommInterface = NULL;
			pendingEmulationLevel = WifiEmulationLevel_Off;
		}
		else
		{
			// Set the current MAC address based on the selected MAC mode.
			switch (selectedMACMode)
			{
				case WifiMACMode_Automatic:
					this->GenerateMACFromValues(FW_Mac);
					NDS_PatchFirmwareMAC();
					break;
					
				case WifiMACMode_Manual:
					this->CopyMACFromUserValues(FW_Mac);
					NDS_PatchFirmwareMAC();
					break;
					
				case WifiMACMode_ReadFromFirmware:
					memcpy(FW_Mac, (MMU.fw.data + 0x36), 6);
					break;
			}
			
			// Starting the comm interface was successful.
			// Report the MAC address to the user as confirmation.
			printf("WIFI: MAC = %02X:%02X:%02X:%02X:%02X:%02X\n",
				   FW_Mac[0], FW_Mac[1], FW_Mac[2], FW_Mac[3], FW_Mac[4], FW_Mac[5]);
		}
	}
	
	this->_currentEmulationLevel = pendingEmulationLevel;
	
	return result;
}

void WifiHandler::CommStop()
{
	if (this->_currentCommInterface != NULL)
	{
		this->_currentCommInterface->Stop();
		this->_currentCommInterface = NULL;
	}
}

void WifiHandler::CommSendPacket(void *data, size_t len)
{
	if (this->_currentCommInterface != NULL)
	{
		this->_currentCommInterface->SendPacket(data, len);
	}
}

void WifiHandler::CommTrigger()
{
	if (this->_currentCommInterface != NULL)
	{
		this->_currentCommInterface->Trigger();
	}
}

bool WifiHandler::IsPCapSupported()
{
	return ((this->_pcap != NULL) && (this->_pcap != &dummyPCapInterface));
}

bool WifiHandler::IsSocketsSupported()
{
	return this->_isSocketsSupported;
}

void WifiHandler::SetSocketsSupported(bool isSupported)
{
	this->_isSocketsSupported = isSupported;
}

ClientPCapInterface* WifiHandler::GetPCapInterface()
{
	return this->_pcap;
}

void WifiHandler::SetPCapInterface(ClientPCapInterface *pcapInterface)
{
	this->_pcap = (pcapInterface == NULL) ? &dummyPCapInterface : pcapInterface;
}

WifiMACMode WifiHandler::GetMACModeForComm(WifiCommInterfaceID commID)
{
	if (commID == WifiCommInterfaceID_Infrastructure)
	{
		return this->_infrastructureMACMode;
	}
	
	return this->_adhocMACMode;
}

void WifiHandler::SetMACModeForComm(WifiCommInterfaceID commID, WifiMACMode macMode)
{
	switch (commID)
	{
		case WifiCommInterfaceID_AdHoc:
			this->_adhocMACMode = macMode;
			break;
			
		case WifiCommInterfaceID_Infrastructure:
			this->_infrastructureMACMode = macMode;
			break;
			
		default:
			break;
	}
}

uint32_t WifiHandler::GetIP4Address()
{
	return this->_ip4Address;
}

void WifiHandler::SetIP4Address(u32 ip4Address)
{
	this->_ip4Address = ip4Address;
}

uint32_t WifiHandler::GetUniqueMACValue()
{
	return this->_uniqueMACValue;
}

void WifiHandler::SetUniqueMACValue(u32 uniqueValue)
{
	this->_uniqueMACValue = uniqueValue;
}

void WifiHandler::GetUserMACValues(u8 *outValue3, u8 *outValue4, u8 *outValue5)
{
	*outValue3 = this->_userMAC[0];
	*outValue4 = this->_userMAC[1];
	*outValue5 = this->_userMAC[2];
}

void WifiHandler::SetUserMACValues(u8 inValue3, u8 inValue4, u8 inValue5)
{
	this->_userMAC[0] = inValue3;
	this->_userMAC[1] = inValue4;
	this->_userMAC[2] = inValue5;
}

void WifiHandler::GenerateMACFromValues(u8 outMAC[6])
{
	if (outMAC == NULL)
	{
		return;
	}
	
	// If the user passes in 0.0.0.0 for ip4Address, then use the default IP
	// address of 127.0.0.1. Otherwise, just use whatever the user gave us.
	const u32 ipAddr = (this->_ip4Address == 0) ? 0x0100007F : this->_ip4Address;
	
	// If the user passes in 0 for uniqueID, then simply generate a random
	// number for hash. Yes, we are using rand() here, which is not a very
	// good RNG, but at least it's better than nothing.
	u32 hash = (this->_uniqueMACValue == 0) ? (u32)rand() : this->_uniqueMACValue;
	
	while ((hash & 0xFF000000) == 0)
	{
		hash <<= 1;
	}
	
	hash >>= 1;
	hash += ipAddr >> 8;
	hash &= 0x00FFFFFF;
	
	outMAC[3] = hash >> 16;
	outMAC[4] = (hash >> 8) & 0xFF;
	outMAC[5] = hash & 0xFF;
}

void WifiHandler::CopyMACFromUserValues(u8 outMAC[6])
{
	if (outMAC == NULL)
	{
		return;
	}
	
	// The first three MAC values are Nintendo's signature MAC values.
	// The last three MAC values are user-defined.
	outMAC[0] = 0x00;
	outMAC[1] = 0x09;
	outMAC[2] = 0xBF;
	outMAC[3] = this->_userMAC[0];
	outMAC[4] = this->_userMAC[1];
	outMAC[5] = this->_userMAC[2];
}

void WifiHandler::PrepareSaveStateWrite()
{
	WIFI_IOREG_MAP &io = this->_wifi.io;
	RF2958_IOREG_MAP &rf = this->_wifi.rf;
	
	legacyWifiSF.powerOn				= (u32)(io.POWERSTATE.IsPowerOff == 0);
	legacyWifiSF.powerOnPending			= (u32)(io.POWERSTATE.WillPowerOn != 0);
	
	legacyWifiSF.rfStatus				= io.RF_STATUS.value;
	legacyWifiSF.rfPins					= io.RF_PINS.value;
	
	legacyWifiSF.IE						= io.IE.value;
	legacyWifiSF.IF						= io.IF.value;
	
	legacyWifiSF.macMode				= io.MODE_RST.value;
	legacyWifiSF.wepMode				= io.MODE_WEP.value;
	legacyWifiSF.WEP_enable				= io.WEP_CNT.Enable;
	
	legacyWifiSF.TXCnt					= io.TXREQ_READ.value;
	legacyWifiSF.TXStat					= io.TXSTAT.value;
	
	legacyWifiSF.RXCnt					= io.RXCNT.value;
	legacyWifiSF.RXCheckCounter			= 0;
	
	legacyWifiSF.macAddr[0]				= io.MACADDR[0];
	legacyWifiSF.macAddr[1]				= io.MACADDR[1];
	legacyWifiSF.macAddr[2]				= io.MACADDR[2];
	legacyWifiSF.macAddr[3]				= io.MACADDR[3];
	legacyWifiSF.macAddr[4]				= io.MACADDR[4];
	legacyWifiSF.macAddr[5]				= io.MACADDR[5];
	legacyWifiSF.bssid[0]				= io.BSSID[0];
	legacyWifiSF.bssid[1]				= io.BSSID[1];
	legacyWifiSF.bssid[2]				= io.BSSID[2];
	legacyWifiSF.bssid[3]				= io.BSSID[3];
	legacyWifiSF.bssid[4]				= io.BSSID[4];
	legacyWifiSF.bssid[5]				= io.BSSID[5];
	
	legacyWifiSF.aid					= io.AID_FULL.AssociationID;
	legacyWifiSF.pid					= io.AID_LOW.PlayerID;
	legacyWifiSF.retryLimit				= io.TX_RETRYLIMIT.value;
	
	legacyWifiSF.crystalEnabled			= (u32)(io.POWER_US.Disable == 0);
	legacyWifiSF.usec					= io.US_COUNT;
	legacyWifiSF.usecEnable				= io.US_COUNTCNT.EnableCounter;
	legacyWifiSF.ucmp					= io.US_COMPARE;
	legacyWifiSF.ucmpEnable				= io.US_COMPARECNT.EnableCompare;
	legacyWifiSF.eCount					= this->_wifi.cmdCount_u32;
	legacyWifiSF.eCountEnable			= io.CMD_COUNTCNT.EnableCounter;
	
	legacyWifiSF.rf_cfg1				= rf.CFG1.value;
	legacyWifiSF.rf_ifpll1				= rf.IFPLL1.value;
	legacyWifiSF.rf_ifpll2				= rf.IFPLL2.value;
	legacyWifiSF.rf_ifpll3				= rf.IFPLL3.value;
	legacyWifiSF.rf_rfpll1				= rf.RFPLL1.value;
	legacyWifiSF.rf_rfpll2				= rf.RFPLL2.value;
	legacyWifiSF.rf_rfpll3				= rf.RFPLL3.value;
	legacyWifiSF.rf_rfpll4				= rf.RFPLL4.value;
	legacyWifiSF.rf_cal1				= rf.CAL1.value;
	legacyWifiSF.rf_txrx1				= rf.TXRX1.value;
	legacyWifiSF.rf_pcnt1				= rf.PCNT1.value;
	legacyWifiSF.rf_pcnt2				= rf.PCNT2.value;
	legacyWifiSF.rf_vcot1				= rf.VCOT1.value;
	
	legacyWifiSF.rfIOCnt				= io.RF_CNT.value;
	legacyWifiSF.rfIOStatus				= io.RF_BUSY.Busy;
	legacyWifiSF.rfIOData				= (io.RF_DATA2.value << 16) | io.RF_DATA1.value;
	legacyWifiSF.bbIOCnt				= io.BB_CNT.value;
	
	legacyWifiSF.rxRangeBegin			= io.RXBUF_BEGIN;
	legacyWifiSF.rxRangeEnd				= io.RXBUF_END;
	legacyWifiSF.rxWriteCursor			= io.RXBUF_WRCSR.HalfwordAddress;
	legacyWifiSF.rxReadCursor			= io.RXBUF_READCSR.HalfwordAddress;
	legacyWifiSF.rxUnits				= 0;
	legacyWifiSF.rxBufCount				= io.RXBUF_COUNT.Count;
	legacyWifiSF.circBufReadAddress		= io.RXBUF_RD_ADDR.ByteAddress;
	legacyWifiSF.circBufWriteAddress	= io.TXBUF_WR_ADDR.ByteAddress;
	legacyWifiSF.circBufReadEnd			= io.RXBUF_GAP.ByteAddress;
	legacyWifiSF.circBufReadSkip		= io.RXBUF_GAPDISP.HalfwordOffset;
	legacyWifiSF.circBufWriteEnd		= io.TXBUF_GAP.ByteAddress;
	legacyWifiSF.circBufWriteSkip		= io.TXBUF_GAPDISP.HalfwordOffset;
	
	legacyWifiSF.randomSeed				= io.RANDOM.Random;
	
	memcpy(legacyWifiSF.wifiIOPorts, &this->_wifi.io, sizeof(WIFI_IOREG_MAP));
	memcpy(legacyWifiSF.bb_data, this->_wifi.bb.data, sizeof(this->_wifi.bb.data));
	memcpy(legacyWifiSF.wifiRAM, this->_wifi.RAM, sizeof(this->_wifi.RAM));
}

void WifiHandler::ParseSaveStateRead()
{
	RF2958_IOREG_MAP &rf = this->_wifi.rf;
	rf.CFG1.value		= legacyWifiSF.rf_cfg1;
	rf.IFPLL1.value		= legacyWifiSF.rf_ifpll1;
	rf.IFPLL2.value		= legacyWifiSF.rf_ifpll2;
	rf.IFPLL3.value		= legacyWifiSF.rf_ifpll3;
	rf.RFPLL1.value		= legacyWifiSF.rf_rfpll1;
	rf.RFPLL2.value		= legacyWifiSF.rf_rfpll2;
	rf.RFPLL3.value		= legacyWifiSF.rf_rfpll3;
	rf.RFPLL4.value		= legacyWifiSF.rf_rfpll4;
	rf.CAL1.value		= legacyWifiSF.rf_cal1;
	rf.TXRX1.value		= legacyWifiSF.rf_txrx1;
	rf.PCNT1.value		= legacyWifiSF.rf_pcnt1;
	rf.PCNT2.value		= legacyWifiSF.rf_pcnt2;
	rf.VCOT1.value		= legacyWifiSF.rf_vcot1;
	
	memcpy(&this->_wifi.io, legacyWifiSF.wifiIOPorts, sizeof(WIFI_IOREG_MAP));
	memcpy(this->_wifi.bb.data, legacyWifiSF.bb_data, sizeof(this->_wifi.bb.data));
	memcpy(this->_wifi.RAM, legacyWifiSF.wifiRAM, sizeof(this->_wifi.RAM));
}
