Home | History | Annotate | Download | only in WinQuake
      1 /*
      2 Copyright (C) 1996-1997 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // net_ipx.c
     21 
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <dpmi.h>
     25 
     26 #include "quakedef.h"
     27 #include "dosisms.h"
     28 #include "net_ipx.h"
     29 
     30 #define	EIO		 		5	/* I/O error */
     31 
     32 #define AF_NETWARE 		64
     33 
     34 #define IPX_OPEN					0
     35 #define IPX_CLOSE					1
     36 #define IPX_GETROUTE				2
     37 #define IPX_SEND					3
     38 #define IPX_LISTEN					4
     39 #define IPX_SCHEDULEEVENT			5
     40 #define IPX_CANCEL					6
     41 #define IPX_SCHEDULESPECIALEVENT	7
     42 #define IPX_GETINTERVALMARKER		8
     43 #define IPX_GETADDRESS				9
     44 #define IPX_RELINQUISH				10
     45 
     46 #define PTYPE_UNKNOWN				0
     47 #define PTYPE_RIP					1
     48 #define PTYPE_ECHO					2
     49 #define PTYPE_ERROR					3
     50 #define PTYPE_IPX					4
     51 #define PTYPE_SPX					5
     52 
     53 #pragma pack(1)
     54 
     55 typedef struct
     56 {
     57 	byte	network[4];
     58 	byte	node[6];
     59 	short	socket;
     60 } IPXaddr;
     61 
     62 struct sockaddr_ipx
     63 {
     64     short			sipx_family;
     65 	IPXaddr			sipx_addr;
     66     char			sipx_zero[2];
     67 };
     68 #define sipx_port sipx_addr.socket
     69 
     70 typedef struct
     71 {
     72 	short			checkSum;
     73 	short			length;
     74 	byte			transportControl;
     75 	byte			type;
     76 	IPXaddr			destination;
     77 	IPXaddr			source;
     78 } IPXheader;
     79 
     80 typedef struct ECBStructure
     81 {
     82 	struct ECBStructure *link;
     83 	unsigned short	ESR_off;
     84 	unsigned short	ESR_seg;
     85 	byte	inUse;
     86 	byte	completionCode;
     87 	short	socket;
     88 	byte	IPXWorkspace[4];
     89 	byte	driverWorkspace[12];
     90 	byte	immediateAddress[6];
     91 	short	fragCount;
     92 	short	fragOff;
     93 	short	fragSeg;
     94 	short	fragSize;
     95 } ECB;
     96 
     97 #pragma pack()
     98 
     99 typedef struct
    100 {
    101 	ECB			ecb;
    102 	IPXheader	header;
    103 	int			sequence;
    104 	char		data[NET_DATAGRAMSIZE];
    105 } ipx_lowmem_buffer_t;
    106 
    107 #define LOWMEMSIZE		(100 * 1024)
    108 #define LOWMEMSAVE		256
    109 #define IPXBUFFERS		((LOWMEMSIZE - LOWMEMSAVE)/ sizeof(ipx_lowmem_buffer_t))
    110 #define IPXSOCKBUFFERS	5
    111 #define IPXSOCKETS		(IPXBUFFERS / IPXSOCKBUFFERS)
    112 
    113 // each socket's socketbuffer 0 is used for sending, the others for listening
    114 
    115 typedef struct
    116 {
    117 	char				reserved[LOWMEMSAVE];
    118 	ipx_lowmem_buffer_t	socketbuffer[IPXSOCKETS][IPXSOCKBUFFERS];
    119 } ipx_lowmem_area_t;
    120 
    121 
    122 static int ipxsocket[IPXSOCKETS];
    123 static ECB *readlist[IPXSOCKETS];
    124 static int sequence[IPXSOCKETS];
    125 static int handlesInUse;
    126 static ipx_lowmem_area_t *lma;
    127 static char *lowmem_buffer;
    128 static int lowmem_bufseg;
    129 static int lowmem_bufoff;
    130 static unsigned short ipx_cs;
    131 static unsigned short ipx_ip;
    132 static int net_acceptsocket = -1;
    133 static int net_controlsocket;
    134 
    135 static void IPX_PollProcedure(void);
    136 static PollProcedure pollProcedure = {NULL, 0.0, IPX_PollProcedure};
    137 
    138 //=============================================================================
    139 
    140 static void IPX_GetLocalAddress(IPXaddr *addr)
    141 {
    142 	regs.x.cs = ipx_cs;
    143 	regs.x.ip = ipx_ip;
    144 	regs.x.bx = IPX_GETADDRESS;
    145 	regs.x.es = lowmem_bufseg;
    146 	regs.x.si = lowmem_bufoff;
    147 	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
    148 	Q_memcpy(addr, lowmem_buffer, 10);
    149 }
    150 
    151 //=============================================================================
    152 
    153 static int IPX_GetLocalTarget(IPXaddr *addr, byte *localTarget)
    154 {
    155 	regs.x.cs = ipx_cs;
    156 	regs.x.ip = ipx_ip;
    157 	regs.x.bx = IPX_GETROUTE;
    158 	regs.x.es = lowmem_bufseg;
    159 	regs.x.si = lowmem_bufoff;
    160 	regs.x.di = lowmem_bufoff + sizeof(IPXaddr);
    161 	Q_memcpy(lowmem_buffer, addr, sizeof(IPXaddr));
    162 	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
    163 	if (regs.h.al)
    164 		return -1;
    165 	Q_memcpy(localTarget, lowmem_buffer + sizeof(IPXaddr), 6);
    166 	return 0;
    167 }
    168 
    169 //=============================================================================
    170 
    171 static void IPX_ListenForPacket(ECB *ecb)
    172 {
    173 	regs.x.cs = ipx_cs;
    174 	regs.x.ip = ipx_ip;
    175 	regs.x.bx = IPX_LISTEN;
    176 	regs.x.es = ptr2real(ecb) >> 4;
    177 	regs.x.si = ptr2real(ecb) & 0xf;
    178 	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
    179 }
    180 
    181 //=============================================================================
    182 
    183 static void IPX_RelinquishControl(void)
    184 {
    185 	regs.x.cs = ipx_cs;
    186 	regs.x.ip = ipx_ip;
    187 	regs.x.bx = IPX_RELINQUISH;
    188 	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
    189 }
    190 
    191 
    192 void IPX_PollProcedure(void)
    193 {
    194 	IPX_RelinquishControl();
    195 	SchedulePollProcedure(&pollProcedure, 0.01);
    196 }
    197 
    198 //=============================================================================
    199 
    200 static void ProcessReadyList(int s)
    201 {
    202 	int n;
    203 	ECB *ecb;
    204 	ECB *prev;
    205 
    206 	for (n = 1; n < IPXSOCKBUFFERS; n++)
    207 	{
    208 		if (lma->socketbuffer[s][n].ecb.inUse == 0)
    209 		{
    210 			for (ecb = readlist[s], prev = NULL; ecb; ecb = ecb->link)
    211 			{
    212 				if (lma->socketbuffer[s][n].sequence < ((ipx_lowmem_buffer_t *) ecb)->sequence)
    213 					break;
    214 				prev = ecb;
    215 			}
    216 			if (ecb)
    217 				lma->socketbuffer[s][n].ecb.link = ecb;
    218 			else
    219 				lma->socketbuffer[s][n].ecb.link = NULL;
    220 			if (prev)
    221 				prev->link = &lma->socketbuffer[s][n].ecb;
    222 			else
    223 				readlist[s] = &lma->socketbuffer[s][n].ecb;
    224 			lma->socketbuffer[s][n].ecb.inUse = 0xff;
    225 		}
    226 	}
    227 }
    228 
    229 //=============================================================================
    230 
    231 int IPX_Init(void)
    232 {
    233 	int s;
    234 	int n;
    235 	struct qsockaddr addr;
    236 	char *colon;
    237 
    238 	if (COM_CheckParm ("-noipx"))
    239 		return -1;
    240 
    241 	// find the IPX far call entry point
    242 	regs.x.ax = 0x7a00;
    243 	__dpmi_simulate_real_mode_interrupt (0x2f, (__dpmi_regs *)&regs);
    244 	if (regs.h.al != 0xff)
    245 	{
    246 		Con_Printf("IPX not detected\n");
    247 		return -1;
    248 	}
    249 	ipx_cs = regs.x.es;
    250 	ipx_ip = regs.x.di;
    251 
    252 	// grab a chunk of memory down in DOS land
    253 	lowmem_buffer = dos_getmemory(LOWMEMSIZE);
    254 	if (!lowmem_buffer)
    255 	{
    256 		Con_Printf("IPX_Init: Not enough low memory\n");
    257 		return -1;
    258 	}
    259 	lowmem_bufoff = ptr2real(lowmem_buffer) & 0xf;
    260 	lowmem_bufseg = ptr2real(lowmem_buffer) >> 4;
    261 
    262 	// init socket handles & buffers
    263 	handlesInUse = 0;
    264 	lma = (ipx_lowmem_area_t *)lowmem_buffer;
    265 	for (s = 0; s < IPXSOCKETS; s++)
    266 	{
    267 		ipxsocket[s] = 0;
    268 		for (n = 0; n < IPXSOCKBUFFERS; n++)
    269 		{
    270 			lma->socketbuffer[s][n].ecb.link = NULL;
    271 			lma->socketbuffer[s][n].ecb.ESR_off = 0;
    272 			lma->socketbuffer[s][n].ecb.ESR_seg = 0;
    273 			lma->socketbuffer[s][n].ecb.socket = 0;
    274 			lma->socketbuffer[s][n].ecb.inUse = 0xff;
    275 			lma->socketbuffer[s][n].ecb.completionCode = 0;
    276 			lma->socketbuffer[s][n].ecb.fragCount = 1;
    277 			lma->socketbuffer[s][n].ecb.fragOff = ptr2real(&lma->socketbuffer[s][n].header) & 0xf;
    278 			lma->socketbuffer[s][n].ecb.fragSeg = ptr2real(&lma->socketbuffer[s][n].header) >> 4;
    279 			lma->socketbuffer[s][n].ecb.fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE;
    280 		}
    281 	}
    282 
    283 	if ((net_controlsocket = IPX_OpenSocket (0)) == -1)
    284 	{
    285 		dos_freememory(lowmem_buffer);
    286 		Con_DPrintf ("IPX_Init: Unable to open control socket\n");
    287 		return -1;
    288 	}
    289 
    290 	SchedulePollProcedure(&pollProcedure, 0.01);
    291 
    292 	IPX_GetSocketAddr (net_controlsocket, &addr);
    293 	Q_strcpy(my_ipx_address,  IPX_AddrToString (&addr));
    294 	colon = Q_strrchr (my_ipx_address, ':');
    295 	if (colon)
    296 		*colon = 0;
    297 
    298 	Con_Printf("IPX initialized\n");
    299 	ipxAvailable = true;
    300 	return net_controlsocket;
    301 }
    302 
    303 //=============================================================================
    304 
    305 void IPX_Shutdown(void)
    306 {
    307 	IPX_Listen (false);
    308 	IPX_CloseSocket (net_controlsocket);
    309 	dos_freememory(lowmem_buffer);
    310 }
    311 
    312 //=============================================================================
    313 
    314 void IPX_Listen (qboolean state)
    315 {
    316 	// enable listening
    317 	if (state)
    318 	{
    319 		if (net_acceptsocket != -1)
    320 			return;
    321 		if ((net_acceptsocket = IPX_OpenSocket (net_hostport)) == -1)
    322 			Sys_Error ("IPX_Listen: Unable to open accept socket\n");
    323 		return;
    324 	}
    325 
    326 	// disable listening
    327 	if (net_acceptsocket == -1)
    328 		return;
    329 	IPX_CloseSocket (net_acceptsocket);
    330 	net_acceptsocket = -1;
    331 }
    332 
    333 //=============================================================================
    334 
    335 int IPX_OpenSocket(int port)
    336 {
    337 	int handle;
    338 	int n;
    339 	unsigned short socket;
    340 
    341 	if (handlesInUse == IPXSOCKETS)
    342 		return -1;
    343 
    344 	// open the IPX socket
    345 	regs.x.cs = ipx_cs;
    346 	regs.x.ip = ipx_ip;
    347 	regs.x.bx = IPX_OPEN;
    348 	regs.h.al = 0;
    349 	regs.x.dx = htons(port);
    350 	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
    351 	if (regs.h.al == 0xfe)
    352 	{
    353 		Con_DPrintf("IPX_OpenSocket: all sockets in use\n");
    354 		return -1;
    355 	}
    356 	if (regs.h.al == 0xff)
    357 	{
    358 		Con_DPrintf("IPX_OpenSocket: socket already open\n");
    359 		return -1;
    360 	}
    361 	if (regs.h.al != 0)
    362 	{
    363 		Con_DPrintf("IPX_OpenSocket: error %02x\n", regs.h.al);
    364 		return -1;
    365 	}
    366 	socket = regs.x.dx;
    367 
    368 // grab a handle; fill in the ECBs, and get them listening
    369 	for (handle = 0; handle < IPXSOCKETS; handle++)
    370 	{
    371 		if (ipxsocket[handle] == 0)
    372 		{
    373 			ipxsocket[handle] = socket;
    374 			readlist[handle] = NULL;
    375 			sequence[handle] = 0;
    376 			for (n = 0; n < IPXSOCKBUFFERS; n ++)
    377 			{
    378 				lma->socketbuffer[handle][n].ecb.socket = socket;
    379 				lma->socketbuffer[handle][n].ecb.inUse = 0;
    380 				if (n)
    381 					IPX_ListenForPacket(&lma->socketbuffer[handle][n].ecb);
    382 			}
    383 			handlesInUse++;
    384 			return handle;
    385 		}
    386 	}
    387 
    388 	// "this will NEVER happen"
    389 	Sys_Error("IPX_OpenSocket: handle allocation failed\n");
    390 	return -1;
    391 }
    392 
    393 //=============================================================================
    394 
    395 int IPX_CloseSocket(int handle)
    396 {
    397 	// if there's a send in progress, give it one last chance
    398 	if (lma->socketbuffer[handle][0].ecb.inUse != 0)
    399 		IPX_RelinquishControl();
    400 
    401 	// close the socket (all pending sends/received are cancelled)
    402 	regs.x.cs = ipx_cs;
    403 	regs.x.ip = ipx_ip;
    404 	regs.x.bx = IPX_CLOSE;
    405 	regs.x.dx = ipxsocket[handle];
    406 	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
    407 
    408 	ipxsocket[handle] = 0;
    409 	handlesInUse--;
    410 
    411 	return 0;
    412 }
    413 
    414 //=============================================================================
    415 
    416 int IPX_Connect (int handle, struct qsockaddr *addr)
    417 {
    418 	IPXaddr ipxaddr;
    419 
    420 	Q_memcpy(&ipxaddr, &((struct sockaddr_ipx *)addr)->sipx_addr, sizeof(IPXaddr));
    421 	if (IPX_GetLocalTarget(&ipxaddr, lma->socketbuffer[handle][0].ecb.immediateAddress) != 0)
    422 	{
    423 		Con_Printf("Get Local Target failed\n");
    424 		return -1;
    425 	}
    426 
    427 	return 0;
    428 }
    429 
    430 //=============================================================================
    431 
    432 int IPX_CheckNewConnections (void)
    433 {
    434 	int n;
    435 
    436 	if (net_acceptsocket == -1)
    437 		return -1;
    438 
    439 	for (n = 1; n < IPXSOCKBUFFERS; n ++)
    440 		if (lma->socketbuffer[net_acceptsocket][n].ecb.inUse == 0)
    441 			return net_acceptsocket;
    442 	return -1;
    443 }
    444 
    445 //=============================================================================
    446 
    447 int IPX_Read (int handle, byte *buf, int len, struct qsockaddr *addr)
    448 {
    449 	ECB		*ecb;
    450 	ipx_lowmem_buffer_t *rcvbuf;
    451 	int		copylen;
    452 
    453 	ProcessReadyList(handle);
    454 tryagain:
    455 	if (readlist[handle] == NULL)
    456 		return 0;
    457 	ecb = readlist[handle];
    458 	readlist[handle] = ecb->link;
    459 
    460 	if (ecb->completionCode != 0)
    461 	{
    462 		Con_Printf("Warning: IPX_Read error %02x\n", ecb->completionCode);
    463 		ecb->fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE;
    464 		IPX_ListenForPacket(ecb);
    465 		goto tryagain;
    466 	}
    467 
    468 	rcvbuf = (ipx_lowmem_buffer_t *)ecb;
    469 
    470 	// copy the data up to the buffer
    471 	copylen = ntohs(rcvbuf->header.length) - (sizeof(int) + sizeof(IPXheader));
    472 	if (len < copylen)
    473 		Sys_Error("IPX_Read: buffer too small (%d vs %d)\n", len, copylen);
    474 	Q_memcpy(buf, rcvbuf->data, copylen);
    475 
    476 	// fill in the addr if they want it
    477 	if (addr)
    478 	{
    479 		((struct sockaddr_ipx *)addr)->sipx_family = AF_NETWARE;
    480 		Q_memcpy(&((struct sockaddr_ipx *)addr)->sipx_addr, rcvbuf->header.source.network, sizeof(IPXaddr));
    481 		((struct sockaddr_ipx *)addr)->sipx_zero[0] = 0;
    482 		((struct sockaddr_ipx *)addr)->sipx_zero[1] = 0;
    483 	}
    484 
    485 	// update the send ecb's immediate address
    486 	Q_memcpy(lma->socketbuffer[handle][0].ecb.immediateAddress, rcvbuf->ecb.immediateAddress, 6);
    487 
    488 	// get this ecb listening again
    489 	rcvbuf->ecb.fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE;
    490 	IPX_ListenForPacket(&rcvbuf->ecb);
    491 	return copylen;
    492 }
    493 
    494 //=============================================================================
    495 
    496 int IPX_Broadcast (int handle, byte *buf, int len)
    497 {
    498 	struct sockaddr_ipx addr;
    499 	int ret;
    500 
    501 	Q_memset(addr.sipx_addr.network, 0x00, 4);
    502 	Q_memset(addr.sipx_addr.node, 0xff, 6);
    503 	addr.sipx_port = htons(net_hostport);
    504 	Q_memset(lma->socketbuffer[handle][0].ecb.immediateAddress, 0xff, 6);
    505 	ret = IPX_Write (handle, buf, len, (struct qsockaddr *)&addr);
    506 	return ret;
    507 }
    508 
    509 //=============================================================================
    510 
    511 int IPX_Write (int handle, byte *buf, int len, struct qsockaddr *addr)
    512 {
    513 	// has the previous send completed?
    514 	while (lma->socketbuffer[handle][0].ecb.inUse != 0)
    515 		IPX_RelinquishControl();
    516 
    517 	switch (lma->socketbuffer[handle][0].ecb.completionCode)
    518 	{
    519 		case 0x00: // success
    520 		case 0xfc: // request cancelled
    521 			break;
    522 
    523 		case 0xfd: // malformed packet
    524 		default:
    525 			Con_Printf("IPX driver send failure: %02x\n", lma->socketbuffer[handle][0].ecb.completionCode);
    526 			break;
    527 
    528 		case 0xfe: // packet undeliverable
    529 		case 0xff: // unable to send packet
    530 			Con_Printf("IPX lost route, trying to re-establish\n");
    531 
    532 			// look for a new route
    533 			if (IPX_GetLocalTarget (&lma->socketbuffer[handle][0].header.destination, lma->socketbuffer[handle][0].ecb.immediateAddress) != 0)
    534 				return -1;
    535 
    536 			// re-send the one that failed
    537 			regs.x.cs = ipx_cs;
    538 			regs.x.ip = ipx_ip;
    539 			regs.x.bx = IPX_SEND;
    540 			regs.x.es = ptr2real(&lma->socketbuffer[handle][0].ecb) >> 4;
    541 			regs.x.si = ptr2real(&lma->socketbuffer[handle][0].ecb) & 0xf;
    542 			__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
    543 
    544 			// report that we did not send the current one
    545 			return 0;
    546 	}
    547 
    548 	// ecb : length
    549 	lma->socketbuffer[handle][0].ecb.fragSize = sizeof(IPXheader) + sizeof(int) + len;
    550 
    551 	// ipx header : type
    552 	lma->socketbuffer[handle][0].header.type = PTYPE_IPX;
    553 
    554 	// ipx header : destination
    555 	Q_memcpy(&lma->socketbuffer[handle][0].header.destination, &((struct sockaddr_ipx *)addr)->sipx_addr, sizeof(IPXaddr));
    556 
    557 	// sequence number
    558 	lma->socketbuffer[handle][0].sequence = sequence[handle];
    559 	sequence[handle]++;
    560 
    561 	// copy down the data
    562 	Q_memcpy(lma->socketbuffer[handle][0].data, buf, len);
    563 
    564 	regs.x.cs = ipx_cs;
    565 	regs.x.ip = ipx_ip;
    566 	regs.x.bx = IPX_SEND;
    567 	regs.x.es = ptr2real(&lma->socketbuffer[handle][0].ecb) >> 4;
    568 	regs.x.si = ptr2real(&lma->socketbuffer[handle][0].ecb) & 0xf;
    569 	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
    570 
    571 	return len;
    572 }
    573 
    574 //=============================================================================
    575 
    576 char *IPX_AddrToString (struct qsockaddr *addr)
    577 {
    578 	static char buf[28];
    579 
    580 	sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u",
    581 		((struct sockaddr_ipx *)addr)->sipx_addr.network[0],
    582 		((struct sockaddr_ipx *)addr)->sipx_addr.network[1],
    583 		((struct sockaddr_ipx *)addr)->sipx_addr.network[2],
    584 		((struct sockaddr_ipx *)addr)->sipx_addr.network[3],
    585 		((struct sockaddr_ipx *)addr)->sipx_addr.node[0],
    586 		((struct sockaddr_ipx *)addr)->sipx_addr.node[1],
    587 		((struct sockaddr_ipx *)addr)->sipx_addr.node[2],
    588 		((struct sockaddr_ipx *)addr)->sipx_addr.node[3],
    589 		((struct sockaddr_ipx *)addr)->sipx_addr.node[4],
    590 		((struct sockaddr_ipx *)addr)->sipx_addr.node[5],
    591 		ntohs(((struct sockaddr_ipx *)addr)->sipx_port)
    592 		);
    593 	return buf;
    594 }
    595 
    596 //=============================================================================
    597 
    598 int IPX_StringToAddr (char *string, struct qsockaddr *addr)
    599 {
    600 	int  val;
    601 	char buf[3];
    602 
    603 	buf[2] = 0;
    604 	Q_memset(addr, 0, sizeof(struct qsockaddr));
    605 	addr->sa_family = AF_NETWARE;
    606 
    607 #define DO(src,dest)	\
    608 	buf[0] = string[src];	\
    609 	buf[1] = string[src + 1];	\
    610 	if (sscanf (buf, "%x", &val) != 1)	\
    611 		return -1;	\
    612 	((struct sockaddr_ipx *)addr)->sipx_addr.dest = val
    613 
    614 	DO(0, network[0]);
    615 	DO(2, network[1]);
    616 	DO(4, network[2]);
    617 	DO(6, network[3]);
    618 	DO(9, node[0]);
    619 	DO(11, node[1]);
    620 	DO(13, node[2]);
    621 	DO(15, node[3]);
    622 	DO(17, node[4]);
    623 	DO(19, node[5]);
    624 #undef DO
    625 
    626 	sscanf (&string[22], "%u", &val);
    627 	((struct sockaddr_ipx *)addr)->sipx_port = htons(val);
    628 
    629 	return 0;
    630 }
    631 
    632 //=============================================================================
    633 
    634 int IPX_GetSocketAddr (int handle, struct qsockaddr *addr)
    635 {
    636 	Q_memset(addr, 0, sizeof(struct qsockaddr));
    637 	addr->sa_family = AF_NETWARE;
    638 	IPX_GetLocalAddress(&((struct sockaddr_ipx *)addr)->sipx_addr);
    639 	((struct sockaddr_ipx *)addr)->sipx_port = ipxsocket[handle];
    640 	return 0;
    641 }
    642 
    643 //=============================================================================
    644 
    645 int IPX_GetNameFromAddr (struct qsockaddr *addr, char *name)
    646 {
    647 	Q_strcpy(name, IPX_AddrToString(addr));
    648 	return 0;
    649 }
    650 
    651 //=============================================================================
    652 
    653 int IPX_GetAddrFromName (char *name, struct qsockaddr *addr)
    654 {
    655 	int n;
    656 	char buf[32];
    657 
    658 	n = Q_strlen(name);
    659 
    660 	if (n == 12)
    661 	{
    662 		sprintf(buf, "00000000:%s:%u", name, net_hostport);
    663 		return IPX_StringToAddr (buf, addr);
    664 	}
    665 	if (n == 21)
    666 	{
    667 		sprintf(buf, "%s:%u", name, net_hostport);
    668 		return IPX_StringToAddr (buf, addr);
    669 	}
    670 	if (n > 21 && n <= 27)
    671 		return IPX_StringToAddr (name, addr);
    672 
    673 	return -1;
    674 }
    675 
    676 //=============================================================================
    677 
    678 int IPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
    679 {
    680 	if (addr1->sa_family != addr2->sa_family)
    681 		return -1;
    682 
    683 	if(Q_memcmp(&((struct sockaddr_ipx *)addr1)->sipx_addr, &((struct sockaddr_ipx *)addr2)->sipx_addr, 10))
    684 		return -1;
    685 
    686 	if (((struct sockaddr_ipx *)addr1)->sipx_port != ((struct sockaddr_ipx *)addr2)->sipx_port)
    687 		return 1;
    688 
    689 	return 0;
    690 }
    691 
    692 //=============================================================================
    693 
    694 int IPX_GetSocketPort (struct qsockaddr *addr)
    695 {
    696 	return ntohs(((struct sockaddr_ipx *)addr)->sipx_port);
    697 }
    698 
    699 
    700 int IPX_SetSocketPort (struct qsockaddr *addr, int port)
    701 {
    702 	((struct sockaddr_ipx *)addr)->sipx_port = htons(port);
    703 	return 0;
    704 }
    705 
    706 //=============================================================================
    707