/
Crimson2/alias/
Crimson2/area.tmp/
Crimson2/area.tmp/AnomalySpaceDock/
Crimson2/area.tmp/AnomalyStation/
Crimson2/area.tmp/AntHill/
Crimson2/area.tmp/ArcticTerrarium/
Crimson2/area.tmp/BuilderCity/
Crimson2/area.tmp/Dungeon/
Crimson2/area.tmp/MiningDock/
Crimson2/area.tmp/PipeSystem/
Crimson2/area.tmp/RattArea/
Crimson2/area.tmp/RobotFactory/
Crimson2/area.tmp/SilverDale/
Crimson2/area.tmp/StarshipFearless/
Crimson2/area.tmp/StationConduits/
Crimson2/area.tmp/TerrariumAlpha/
Crimson2/area.tmp/TerrariumBeta/
Crimson2/area.tmp/TestArea/
Crimson2/area.tmp/Void/
Crimson2/area/
Crimson2/area/AnomalySpaceDock/
Crimson2/area/AnomalyStation/
Crimson2/area/MiningDock/
Crimson2/area/PipeSystem/
Crimson2/area/SilverDale/
Crimson2/area/StationConduits/
Crimson2/area/Void/
Crimson2/board/
Crimson2/clone/
Crimson2/lib/
Crimson2/mole/
Crimson2/mole/mole_src/HELP/
Crimson2/player/
Crimson2/util/
Crimson2/wldedit/
Crimson2/wldedit/res/
// MOLE Client for MS Windows 3.11
// moleprot.c identifier: mp
// ****************************************************************************
// Copyright (C) B. Cameron Lesiuk, 1997-1999. All rights reserved.
// Permission to use/copy this code is granted for non-commercial use only.
// B. Cameron Lesiuk
// Victoria, BC, Canada
// wi961@freenet.victoria.bc.ca
// ****************************************************************************
//
// Written by B. Cameron Lesiuk
// March, 1997

// This module does the actual MOLE interaction, including buffering and
// queueing of requests and tracking of responses. This module also does
// some socket stuff for sending, unfortunately.

#include<windows.h>
#include<windowsx.h>
#include<stdio.h>
#include<time.h>
#include<string.h>
#include"telnet.h"
#include"moledefs.h"
#include"molem.h"
#include"molerc.h"
#include"dstruct.h"
#include"host.h"
#include"infobox.h"
#include"moleprot.h"
#include"unpack.h"
#include"terminal.h"
#include"timer.h"
#include"debug.h"
#include"edit.h"
#include"areawnd.h"
#include"help.h"

/* Defines */
#define MP_SEND_BUFFER_SIZE 65500L

/* Globals */
unsigned long g_mpIDPool; /* contains our next available packet ID */
MPBUF g_mpPktBuf; /* intermediate buffer to collect a single complete mole packet */
MPBUF g_mpCmdBuf; /* intermediate buffer to collect a packet's data after formatting and checksum checks have verified packet as valid */
MPREQUEST *g_mpRequestQ;
/* the SendBuffer is not a MPBUF type - it's just a huge build-in
 * circular text buffer. It's allocated once and it's static in size. If
 * it fills up - we're probably in trouble anyways. */
char *g_mpSendBuffer; /* buffer for send characters - formatted & mixed mole/normal - HUGE and STATIC */
HGLOBAL g_mpSendBufferMemory;
unsigned long g_mpSendBufferSize;
char *g_mpSendBufferHead,*g_mpSendBufferTail;
unsigned long g_mpHostVersion; /* contains host MOLE and Crimson2 versions */

/* Buffer handling procs */
BOOL mpBufAlloc(MPBUF *p_buf,unsigned long p_len) {
  if (!p_buf) return FALSE;
  p_buf->bMemory=GlobalAlloc(GHND|GMEM_NOCOMPACT,p_len);
  p_buf->bData=(char *)GlobalLock(p_buf->bMemory);
  if ((!p_buf->bData)||(!p_buf->bMemory)) {
    ibInfoBox(g_aahWnd,"Cannot allocate memory for buffers. Please Free some memory!","Whoops!",IB_OK,NULL,0L);
    return FALSE;
  }
  p_buf->bLen=GlobalSize(p_buf->bMemory);
  p_buf->bPos=0L;
  return TRUE;
}

void mpBufFree(MPBUF *p_buf) {
  if (!p_buf) return;
  GlobalUnlock(p_buf->bMemory);
  GlobalFree(p_buf->bMemory);
  p_buf->bMemory=NULL;
  p_buf->bData=NULL;
  p_buf->bLen=p_buf->bPos=0L;
  return;
}

BOOL mpBufReAlloc(MPBUF *p_buf,unsigned long p_len) {
  HGLOBAL l_GlobalTemp;
  unsigned long l_i;
  char *l_BufTemp;

  if (!p_buf) return FALSE;
  if (p_len<p_buf->bLen) return TRUE;
  /* first, allocate a new buffer */
  l_GlobalTemp=GlobalAlloc(GHND|GMEM_NOCOMPACT,p_len);
  l_BufTemp=(char *)GlobalLock(l_GlobalTemp);
  if ((!l_BufTemp)||(!l_GlobalTemp)) {
    ibInfoBox(g_aahWnd,"Cannot allocate memory for buffers. Please Free some memory!","Whoops!",IB_OK,NULL,0L);
    return FALSE;
  }
  /* next, copy existing data over */
  for (l_i=0;l_i<p_buf->bPos;l_i++)
    l_BufTemp[(unsigned int)l_i]=p_buf->bData[(unsigned int)l_i];
  /* now free our old buffer */
  GlobalUnlock(p_buf->bMemory);
  GlobalFree(p_buf->bMemory);
  /* and move our new buffer into place */
  p_buf->bMemory=l_GlobalTemp;
  p_buf->bData=l_BufTemp;
  return TRUE;
}

void mpBufAppend(MPBUF *p_buf,char *p_data,unsigned long p_datalen) {
  unsigned int l_i;
  if ((!p_buf) || (!p_data)) return;
  if (!mpBufReAlloc(p_buf,p_datalen+p_buf->bPos)) /* resize buffer if required */
    return; /* couldn't allocate memory - abort operation */
  for (l_i=0;l_i<p_datalen;l_i++,p_buf->bPos++)
    p_buf->bData[(unsigned int)(p_buf->bPos)]=p_data[l_i];
}

void mpBufFlush(MPBUF *p_buf) {
  p_buf->bPos=0L;
}

/* Request handling functions */
/* This proc assumes that the request is NOT linked into any request list */
void mpReqFree(MPREQUEST *p_req) {
  HGLOBAL l_GlobalTemp;

  if (!p_req) return;

  /* free data memory */
  if (p_req->rDataMemory) {
    GlobalUnlock(p_req->rDataMemory);
    GlobalFree(p_req->rDataMemory);
  }

  /* and free this request's memory */
  l_GlobalTemp=p_req->rMemory;
  GlobalUnlock(l_GlobalTemp);
  GlobalFree(l_GlobalTemp);
  return;
}

/* This proc writes p_data into p_buf as hexadecimal text */
void mpWriteBinToText(char *p_buf,char p_data) {
  char l_tmp;

  if (!p_buf) return;

  l_tmp=(char)((p_data&0xF0)>>4);
  if (l_tmp<10) { /* numerical */
    *p_buf=(char)(l_tmp+'0');
  } else {
    *p_buf=(char)(l_tmp+'A'-10);
  }
  p_buf++;
  l_tmp=(char)(p_data&0x0F);
  if (l_tmp<10) { /* numerical */
    *p_buf=(char)(l_tmp+'0');
  } else {
    *p_buf=(char)(l_tmp+'A'-10);
  }
}

/* wrapper for mpReqSubmit for external modules */
unsigned long mpReqSubmitEx(unsigned long p_Cmd,char *p_Data,unsigned long p_DataLen) {
  MPREQUEST *l_req;

  mpReqSubmit(l_req=mpReqAlloc(p_Cmd,p_Data,p_DataLen),&g_mpRequestQ);
  return (l_req)?l_req->rID:MOLE_PKID_NULL;

}

/* wrapper for mpReqSubmit for external modules needing numerical packets */
unsigned long mpReqSubmitNum(unsigned long p_Cmd,unsigned long p_Data) {
  char l_buf[5];
  MPREQUEST *l_req;

  l_buf[0]=(char)(p_Data&0x000000FFL);
  l_buf[1]=(char)((p_Data&0x0000FF00L)>>8);
  l_buf[2]=(char)((p_Data&0x00FF0000L)>>16);
  l_buf[3]=(char)((p_Data&0xFF000000L)>>24);
  mpReqSubmit(l_req=mpReqAlloc(p_Cmd,l_buf,4),&g_mpRequestQ);
  return (l_req)?l_req->rID:MOLE_PKID_NULL;

}

/* Insert request into queue */
/* Note that this is the real kahuna. It takes the request, and inserts it into
 * the queue. */
void mpReqSubmit(MPREQUEST *p_req,MPREQUEST **p_list) {
  char l_buf[(MOLE_PKT_MAX*2)+MOLE_HDR_SIZE+8+4]; /* what we need + 4 for good measure (ie: trailing \n & 0x00 etc.*/
  int l_count,l_pos;
  char *l_src;
  char l_checksum;

  if (!p_req) return;

  /* if this request doesn't have an ID, give it a new one */
  /* NOTE we keep IDs the same for re-transmitted requests so that for
   * really slow connections, multiple requests can be sent, but the
   * first confirmation removes the it from the Q and the other confirmations
   * are discarded */
  if (!p_req->rID) {
    /* The first thing we do is assign a unique ID to this request */
    p_req->rID=g_mpIDPool;
    g_mpIDPool++;
    if (!g_mpIDPool) g_mpIDPool=MOLE_PKID_START; /* check for wrap around */
      /* like there's ever going to be wrap around with a 32-bit unsigned int! */
      /* how long would that take? Let's see... even if we somehow generated   */
      /* 100 packets per second, we'd need to run for over 1 year, 4 months    */
      /* straight. I suspect this client, or the machine it's running on, will */
      /* crash way way way before then. Ah, windows. Being able to COUNT on    */
      /* crashes is SUCH a lovely thing. Fuck, whatever.                       */
    }
  p_req->rNext=*p_list;
  p_req->rPrevious=NULL;
  if (*p_list)
    (*p_list)->rPrevious=p_req;
  *p_list=p_req;
  /* time stamp request */
  p_req->rTime=time(NULL);
  /* and fire off our data to the socket. */
  /* This step is a little ugly; we do all the MOLE formatting here. */
  l_pos=MOLE_HDR_SIZE+6; /* this counts where we are in our buffer */
  l_count=0; /* this counts # bytes sent - counts up to MOLE_PKT_MAX */
  l_src=p_req->rData; /* this tracks our source data buffer */
  strcpy(l_buf,MOLE_HDR); /* copy in header - doesn't change anyways */
  l_buf[MOLE_HDR_SIZE]=' ';
  l_buf[MOLE_HDR_SIZE+1]=(char)((MOLE_CMD_MORE&0xFF000000L)>>24);
  l_buf[MOLE_HDR_SIZE+2]=(char)((MOLE_CMD_MORE&0x00FF0000L)>>16);
  l_buf[MOLE_HDR_SIZE+3]=(char)((MOLE_CMD_MORE&0x0000FF00L)>>8);
  l_buf[MOLE_HDR_SIZE+4]=(char)((MOLE_CMD_MORE&0x000000FFL));
  l_buf[MOLE_HDR_SIZE+5]=' ';
  l_checksum=0;

  /* note that first we have to insert our packet ID */
  /* Note that for data fields, LSB comes FIRST, MSB comes LAST */
  mpWriteBinToText(&(l_buf[l_pos]),(char)(p_req->rID&0x000000FFL));
  l_checksum^=(char)(p_req->rID&0x000000FFL);
  l_pos+=2;
  mpWriteBinToText(&(l_buf[l_pos]),(char)((p_req->rID&0x0000FF00L)>>8));
  l_checksum^=(char)((p_req->rID&0x0000FF00L)>>8);
  l_pos+=2;
  mpWriteBinToText(&(l_buf[l_pos]),(char)((p_req->rID&0x00FF0000L)>>16));
  l_checksum^=(char)((p_req->rID&0x00FF0000L)>>16);
  l_pos+=2;
  mpWriteBinToText(&(l_buf[l_pos]),(char)((p_req->rID&0xFF000000L)>>24));
  l_checksum^=(char)((p_req->rID&0xFF000000L)>>24);
  l_pos+=2;
  l_count+=4;

  if (p_req->rData) {
    while((l_src-p_req->rData)<p_req->rDataLen) {
      /* add in this byte */
      mpWriteBinToText(&(l_buf[l_pos]),*l_src);
      l_checksum^=*l_src;
      l_pos+=2;
      l_src++;
      l_count++;

      /* check for maximum packet size */
      if (l_count>=MOLE_PKT_MAX) {
        /* append checksum */
        mpWriteBinToText(&(l_buf[l_pos]),l_checksum);
        l_pos+=2;

        /* and trailing ## */
        l_buf[l_pos]=MOLE_DELIM_CHAR;
        l_pos++;
        l_buf[l_pos]=MOLE_DELIM_CHAR;
        l_pos++;

        /* append a carridge return */
        l_buf[l_pos]='\r';
        l_pos++;
        /* terminate our buffer string */
        l_buf[l_pos]=0;
        /* and copy the sucker into our big ugly send buffer. */
//        dbPrint("Queueing the following data into Send Buffer:");
//        dbPrintNum(l_buf,l_pos);
        mpSendBufferAppend(l_buf,l_pos);

        /* Lastly, reset our local buffer variables */
        l_count=0;
        l_checksum=0;
        l_pos=MOLE_HDR_SIZE+6;
      }
    } /* while */
  } /* if data */
  /* now send off our final data */
  /* append checksum */
  mpWriteBinToText(&(l_buf[l_pos]),l_checksum);
  l_pos+=2;

  /* and trailing ## */
  l_buf[l_pos]=MOLE_DELIM_CHAR;
  l_pos++;
  l_buf[l_pos]=MOLE_DELIM_CHAR;
  l_pos++;

  /* append a carridge return */
  l_buf[l_pos]='\r';
  l_pos++;
  /* terminate our buffer string */
  l_buf[l_pos]=0;
  /* can't forget to put the proper command in! */
  l_buf[MOLE_HDR_SIZE+1]=(char)((p_req->rCmd&0xFF000000L)>>24);
  l_buf[MOLE_HDR_SIZE+2]=(char)((p_req->rCmd&0x00FF0000L)>>16);
  l_buf[MOLE_HDR_SIZE+3]=(char)((p_req->rCmd&0x0000FF00L)>>8);
  l_buf[MOLE_HDR_SIZE+4]=(char)((p_req->rCmd&0x000000FFL));
  /* and copy the sucker into our big ugly send buffer. */
//  dbPrint("Queueing the following data into Send Buffer:");
//  dbPrintNum(l_buf,16);
  mpSendBufferAppend(l_buf,l_pos);
  mpMoleSendData();
  return;
}

/* This proc takes p_req out of the p_list linked list. It doesn't free it though. */
void mpReqExtract(MPREQUEST *p_req,MPREQUEST **p_list) {
  if (!p_req) return;
  if (p_req->rNext)
    p_req->rNext->rPrevious=p_req->rPrevious;
  if (p_req->rPrevious)
    p_req->rPrevious->rNext=p_req->rNext;
  else
    *p_list=p_req->rNext;
  p_req->rPrevious=p_req->rNext=NULL;
}

/* This proc allocates the specified request, and returns a handle to it */
MPREQUEST *mpReqAlloc(unsigned long p_Cmd,char *p_Data,unsigned long p_DataLen) {
  HGLOBAL l_GlobalTemp;
  MPREQUEST *l_req;
  unsigned long l_i;

  /* first, alloc a request structure */
  l_GlobalTemp=GlobalAlloc(GHND|GMEM_NOCOMPACT,sizeof(MPREQUEST));
  l_req=(MPREQUEST *)GlobalLock(l_GlobalTemp);
  if ((!l_GlobalTemp)||(!l_req)) {
    ibInfoBox(g_aahWnd,"Cannot allocate memory for buffers. Please Free some memory!","Whoops!",IB_OK,NULL,0L);
    return NULL;
  }
  l_req->rCmd=p_Cmd;
  l_req->rID=MOLE_PKID_NULL;
  l_req->rMemory=l_GlobalTemp;
  l_req->rTime=0L;
  l_req->rNext=l_req->rPrevious=NULL;
  if (p_Data) {
    l_req->rDataLen=p_DataLen;
    /* now alloc space for our data */
    l_req->rDataMemory=GlobalAlloc(GHND|GMEM_NOCOMPACT,p_DataLen);
    l_req->rData=(char *)GlobalLock(l_req->rDataMemory);
    if ((!l_req->rDataMemory)||(!l_req->rData)) {
      ibInfoBox(g_aahWnd,"Cannot allocate memory for buffers. Please Free some memory!","Whoops!",IB_OK,NULL,0L);
      return NULL;
    }
    /* and copy over data into request structure */
    for (l_i=0;l_i<p_DataLen;l_i++)
      l_req->rData[(unsigned int)l_i]=p_Data[(unsigned int)l_i];
  } else {
    l_req->rDataMemory=NULL;
    l_req->rData=NULL;
    l_req->rDataLen=0L;
  }

  /* and finally, return our new structure pointer */
  return l_req;
}

void mpReqFlush(MPREQUEST **p_list) {
  MPREQUEST *l_req;
  while(*p_list) {
    l_req=*p_list;
    mpReqExtract(l_req,p_list);
    mpReqFree(l_req);
  }
  return;
}

/* Send Buffer Functions */
void mpSendBufferAppend(char *p_buf,unsigned int p_len) {
  unsigned long l_space_left,l_i;
  char *l_p;

//  dbPrint("SendBufferAppend: queue the following data:");
//  dbPrintNum(p_buf,p_len);

  /* first, calculate our remaining space */
  if (g_mpSendBufferHead >= g_mpSendBufferTail)
    l_space_left=g_mpSendBufferSize-(g_mpSendBufferHead-g_mpSendBufferTail);
  else
    l_space_left=(g_mpSendBufferTail-g_mpSendBufferHead);

  if ((p_len+2)>l_space_left) {
    dbPrint("NO ROOM!");
    return; /* just don't append the packet at all. Note that this could really screw some things up.... but oh well */
    /* I'm adding 2 just to leave a little room ... just in case */
  }

//  dbPrint("Copying data over to SendBuffer");
  /* ok, there's room. Copy the sucker over, checking for wrap-around */
  for (l_p=p_buf,l_i=0;l_i<p_len;l_p++,l_i++) {
    *g_mpSendBufferHead=*l_p;
    g_mpSendBufferHead++;
    if ((g_mpSendBufferHead-g_mpSendBuffer)>=g_mpSendBufferSize)
      g_mpSendBufferHead=g_mpSendBuffer;
  }
  /* and we're done. */
  return;
}

void mpSendBufferFlush() {
  g_mpSendBufferHead=g_mpSendBufferTail=g_mpSendBuffer;
  return;
}

unsigned long mpSendBufferSize() {
  unsigned long l_i;
  if (g_mpSendBufferHead >= g_mpSendBufferTail)
    l_i=g_mpSendBufferHead-g_mpSendBufferTail;
  else
    l_i=g_mpSendBufferSize-(g_mpSendBufferTail-g_mpSendBufferHead);
  return l_i;
}

/* read up to p_buflen bytes from buffer */
unsigned long mpSendBufferRead(char *p_buf,unsigned long p_buflen) {
  unsigned long l_i;

  if (!p_buf) return 0L;
  l_i=0;
  while((l_i<p_buflen)&&(g_mpSendBufferTail!=g_mpSendBufferHead)) {
    p_buf[(unsigned int)l_i]=*g_mpSendBufferTail;
    l_i++;
    g_mpSendBufferTail++;
    if ((g_mpSendBufferTail-g_mpSendBuffer)>=g_mpSendBufferSize)
      g_mpSendBufferTail=g_mpSendBuffer;
  }
  return l_i;
}

/* puts data BACK onto the stack - note it has to have JUST been read */
/* because it's not copied - just the pointers are adjusted */
#pragma argsused
unsigned long mpSendBufferUnRead(char *p_buf,unsigned long p_buflen) {
  g_mpSendBufferTail-=(unsigned int)p_buflen;
  if (g_mpSendBufferTail<g_mpSendBuffer)
    g_mpSendBufferTail+=(unsigned int)g_mpSendBufferSize;
  return p_buflen;
}

/* MOLEPROT functions */
BOOL mpInitMoleProt () {
  if (!mpBufAlloc(&g_mpCmdBuf,8192)) return FALSE;
  if (!mpBufAlloc(&g_mpPktBuf,8192)) return FALSE;
  mpBufFlush(&g_mpCmdBuf);
  mpBufFlush(&g_mpPktBuf);
  g_mpRequestQ=NULL;

  /* allocate our huge gigantic output send buffer */
  g_mpSendBufferMemory=GlobalAlloc(GHND|GMEM_NOCOMPACT,MP_SEND_BUFFER_SIZE);
  g_mpSendBuffer=(char *)GlobalLock(g_mpSendBufferMemory);
  if ((!g_mpSendBufferMemory)||(!g_mpSendBuffer)) {
    ibInfoBox(g_aahWnd,"Cannot allocate memory for buffers. Please Free some memory!","Whoops!",IB_OK,NULL,0L);
    return FALSE;
  }
  g_mpSendBufferSize=GlobalSize(g_mpSendBufferMemory);
  g_mpSendBufferHead=g_mpSendBufferTail=g_mpSendBuffer;
  /* Phew. What a monster. */

  g_mpIDPool=MOLE_PKID_START; /* note that 0 is an invalid ID */

  return TRUE;
}

void mpShutdownMoleProt() {
  mpBufFree(&g_mpCmdBuf);
  mpBufFree(&g_mpPktBuf);
  mpReqFlush(&g_mpRequestQ);
//  while (g_mpRequestQ) {
//    l_req=g_mpRequestQ;
//    mpReqExtract(l_req,&g_mpRequestQ);
//    mpReqFree(l_req);
//  }
  GlobalUnlock(g_mpSendBufferMemory);
  GlobalFree(g_mpSendBufferMemory);

  return;
}


/* This proc accepts data form the socket and filters out
 * the mole packets, performing buffering etc. as required.
 * If this proc gets a completed mole packet, it calls the
 * appropriate function and ensures the request management
 * software is notified. */
void mpMolePacketFilter(char *p_buf,unsigned long p_buflen) {
  static l_ParsePacket=FALSE; /* TRUE if we're in the middle of parsing a MOLE packet */
  char *l_p,*l_mark;
  BOOL l_setmark;
  BOOL l_StripReturn=FALSE;

//  dbPrint("mpMolePacketFilter called with new data!");
//  dbPrintNum(p_buf,p_buflen);
  l_p=l_mark=p_buf;
  while ((l_p-p_buf)<p_buflen) {
    l_setmark=FALSE;
    if (l_ParsePacket) {
      if ((*l_p)==MOLE_PKT_STOP) {
//        dbPrint("END OF MOLE PACKET");
        /* end of packet marker */
        /* first, append what data we've gathered here to our mole packet buffer */
        mpBufAppend(&g_mpPktBuf,l_mark,l_p-l_mark);
        /* process our g_mpPktBuf MOLE packet */
        mpStripPkt(&g_mpPktBuf);
        /* reset the packet buffer */
        mpBufFlush(&g_mpPktBuf);
        /* and wrap up - switch back to normal text mode */
        l_StripReturn=TRUE;
        l_setmark=TRUE;
        l_ParsePacket=FALSE;
      }
    } else {
      /* we're looking for a Start-Of-Packet character */
      if ((*l_p)==MOLE_PKT_START) {
//        dbPrint("START OF MOLE PACKET");
        /* mark us as "packet start" and send what we've got so far to our terminal */
//        dbPrint("Sending this junk to the terminal");
        trSendTerminal(l_mark,l_p-l_mark);
//        dbPrintNum(l_mark,l_p-l_mark);
        mpBufFlush(&g_mpPktBuf); /* flush our packet buffer */
        l_setmark=TRUE;
        l_ParsePacket=TRUE;
      }
      /* otherwise, just increment our pointer */
    }
    l_p++;
    if ( (l_StripReturn) && ((*l_p==0x0D)||(*l_p==0x0A)) )
      {
      l_p++;
      if ((*l_p==0x0D)||(*l_p==0x0A))
        {
        l_p++;
        }
      }
    if (l_setmark)
      l_mark=l_p;
  }
  /* we've reached the end of our buffer. Send what we have left to the terminal */
  /* or to the MOLE buffer */
  if (l_ParsePacket) {
    mpBufAppend(&g_mpPktBuf,l_mark,l_p-l_mark);
  } else {
//    dbPrint("Sending this junk to the terminal");
    trSendTerminal(l_mark,l_p-l_mark);
//    dbPrintNum(l_mark,l_p-l_mark);
  }
}

/* when we've buffered in what we think is a complete MOLE packet, */
/* this proc is called to ensure formatting is correct, strip      */
/* extraneous characters, translate data into binary, buffer       */
/* data for _MORE packets, and finally call mpProcessPkt() when    */
/* a complete and proper packet is fully received.                 */
void mpStripPkt(MPBUF *p_pkt) {
  char *l_p;
  unsigned long l_Cmd,l_i;
  char l_data[MOLE_PKT_MAX];
  char l_Checksum;

  if (!p_pkt) return;

//  /* TAG TAG */
//  dbPrint("MOLE PACKET RECEIVED!!!!!!!!!!");
  p_pkt->bData[(unsigned int)(p_pkt->bPos)]=0;
//  dbPrint(p_pkt->bData);

  if (p_pkt->bPos<(MOLE_HDR_SIZE+8)) return;
  l_p=&(p_pkt->bData[MOLE_HDR_SIZE]);
  if (strncmp(p_pkt->bData,MOLE_HDR,MOLE_HDR_SIZE)) return;
  /* read in command */
  l_Cmd=l_p[1];
  l_Cmd<<=8;
  l_Cmd|=l_p[2];
  l_Cmd<<=8;
  l_Cmd|=l_p[3];
  l_Cmd<<=8;
  l_Cmd|=l_p[4];

  /* and now extract data into binary format */
  l_Checksum=0;
  l_p+=6; /* jump to start of data */
  l_i=0; /* our position in l_data[] */
  while(1) {
    if ((l_i+1)>=p_pkt->bLen)
      return; /* premature end of packet - error */
    else if ((*l_p==MOLE_DELIM_CHAR)&&(*(l_p+1)==MOLE_DELIM_CHAR)) {
      /* end of packet. */
      /* check checksum */
      if (l_Checksum) return; /* should be 0x00 */
      /* packet looks good. Let's get out of here. */
      if (l_i) l_i--; /* subtract back before checksum byte */
      break;
    } else {
      /* let's decode this byte */
      if ((*l_p>='0')&&(*l_p<='9')) {
        l_data[(unsigned int)l_i]=(char)((*l_p-'0')<<4);
      } else if ((*l_p>='A')&&(*l_p<='F')) {
        l_data[(unsigned int)l_i]=(char)((*l_p-'A'+0x0A)<<4);
      } else {
        /* error - invalid character */
        return;
      }
      l_p++;
      if ((*l_p>='0')&&(*l_p<='9')) {
        l_data[(unsigned int)l_i]|=(char)((*l_p-'0'));
      } else if ((*l_p>='A')&&(*l_p<='F')) {
        l_data[(unsigned int)l_i]|=(char)((*l_p-'A'+0x0A));
      } else {
        /* error - invalid character */
        return;
      }
      l_Checksum^=l_data[(unsigned int)l_i];
      l_p++;
      l_i++;
    }
  } /* while (1) */

  /* we have our data decoded - append it to our command parameter buffer */
  mpBufAppend(&g_mpCmdBuf,l_data,l_i);

//  dbPrint("Appended data to command buffer - now what?");
  /* now process packet command */
  if (l_Cmd!=MOLE_CMD_MORE) {
//    dbPrint("PROCESS IT!");
    mpProcessPkt(l_Cmd,&g_mpCmdBuf);
    /* clear command buffer */
    mpBufFlush(&g_mpCmdBuf);
  } //else dbPrint("Oh, is a MORE pkt. Wait for more info");

  return;
}

/* This proc handles sending data from our SendBuffer to the socket. */
/* This proc is called when the socket is ready for writing.    */
#define MP_MOLE_SEND_DATA_SIZE 80   /* arbitrary length really */
void mpMoleSendData() {
  char l_buf[MP_MOLE_SEND_DATA_SIZE];
  int l_rc;
  unsigned long l_count;

//  sprintf(l_buf,"Sending data now... (%lu bytes)",mpSendBufferSize());
//  dbPrint(l_buf);
  while (mpSendBufferSize()) {
    l_count=mpSendBufferRead(l_buf,MP_MOLE_SEND_DATA_SIZE);
    if (l_count) {
      /* send to socket */
//      dbPrintNum(l_buf,l_count);
      l_rc=send(g_hoSock,l_buf,(int)l_count,0);
      if (l_rc==SOCKET_ERROR) {
        switch (WSAGetLastError()) {
          case WSAENETRESET:
          case WSAENOTCONN:
          case WSAENOTSOCK:
          case WSAESHUTDOWN:
          case WSAECONNRESET:
          case WSAECONNABORTED:
            /* our socket is dead. */
            hoSocketKilled();
            ibInfoBox(g_aahWnd,"Connection closed due to a network error.","Darn",IB_OK|IB_RUNHELP,NULL,HP_IB_SOCKET_ERROR_NETERR);
            return;
          default:
            mpSendBufferUnRead(l_buf,l_count);
            return;
        } /* if error */
      } else if (l_rc<l_count) {
        mpSendBufferUnRead(&l_buf[l_rc],l_count-l_rc);
      }
    }
  }
  /* now send a TELNET-GA code */
  l_buf[0]=IAC;
  l_buf[1]=GA;
  l_buf[3]=0;
  l_rc=send(g_hoSock,l_buf,2,0);
  if (l_rc==SOCKET_ERROR) {
    switch (WSAGetLastError()) {
      case WSAENETRESET:
      case WSAENOTCONN:
      case WSAENOTSOCK:
      case WSAESHUTDOWN:
      case WSAECONNRESET:
      case WSAECONNABORTED:
        /* our socket is dead. */
        hoSocketKilled();
        ibInfoBox(g_aahWnd,"Connection closed due to a network error.","Darn",IB_OK|IB_RUNHELP,NULL,HP_IB_SOCKET_ERROR_NETERR);
        return;
      default:
        return;
    } /* switch */
  } /* if error */
}

/* This proc strips the first 4 bytes from p_buf and returns them as a decoded
 * unsigned long. No error checking is done - the buffer MUST be 4 or more
 * characters long to avoid errors. */
unsigned long mpDecodeULWORD(char *p_buf) {
  unsigned long l_i;
  char *l_p;

  l_p=p_buf+3;
  l_i=(*l_p)&0xFF;
  l_i<<=8;
  l_p--;
  l_i|=(*l_p)&0xFF;
  l_i<<=8;
  l_p--;
  l_i|=(*l_p)&0xFF;
  l_i<<=8;
  l_p--;
  l_i|=(*l_p)&0xFF;
  return l_i;
}

/* This proc writes the given unsigned long word to the data buffer provided */
/* the return value is the # bytes written to the buffer */
int mpEncodeULWORD(char *p_buf,unsigned long p_data) {
  if (!p_buf) return 0;

  p_buf[0]=(char)((p_data&0x000000FFL));
  p_buf[1]=(char)((p_data&0x0000FF00L)>>8);
  p_buf[2]=(char)((p_data&0x00FF0000L)>>16);
  p_buf[3]=(char)((p_data&0xFF000000L)>>24);
  return 4;
}

/* This proc is part of the RequestQ Loop. It takes incoming packets
 * and matches them with our outgoing request packets. It then removes
 * the outgoing request packets and then takes whatever action is required. */
void mpProcessPkt(unsigned long p_Cmd, MPBUF *p_Data) {
  unsigned long l_pktID;
  unsigned long l_dataLeft;
  unsigned long l_returnCode; /* for NACK messages */
  char *l_p;
  MPREQUEST *l_req,*l_nextReq;
//  char l_buf[100];

  if (!p_Data) {
//    dbPrint("Aborting mpProcessPkt - null pointer");
    return;
  }
//  dbPrint("Processing (now binary) packet...");

  l_dataLeft=p_Data->bPos;
  l_p=p_Data->bData;
  /* first, parse off the packet ID */
  if (l_dataLeft < 4 ) {
//    dbPrint("Less than bytes - can't get packet ID");
    return;
  }

//  sprintf(l_buf,">> %02X %02X %02X %02X ...",l_p[0],l_p[1],l_p[2],l_p[3]);
//  dbPrint(l_buf);

  l_pktID=mpDecodeULWORD(l_p);
  l_p+=4;
  l_dataLeft-=4;

//  sprintf(l_buf,"looking for packet ID=%lu",l_pktID);
//  dbPrint(l_buf);
  /* now, find the requesting packet in our RequestQ */
  for (l_req=g_mpRequestQ;l_req;l_req=l_req->rNext) {
    if (l_req->rID==l_pktID)
      break;
  }

//  dbPrint("Did we find our packet in our Q?");
  if (!l_req) {
//    dbPrint("No... gasp, I'm DIEING");
    return; /* couldn't find packet request - turffed, unsolicited, or corrupted (error) */
  }
//  dbPrint("Yup!");

  l_returnCode=0L;
  /* Ok, let's process our packet, depending on its type */
  switch (p_Cmd) {
    case MOLE_CMD_ACKP:
      /* find the originator and mark it's structure as "up-to-date"
       * instead of "pending" */
      break;
    case MOLE_CMD_NACK:
      /* find the originator and announce that operation was refused */
      if (l_dataLeft >= 4) {
        l_returnCode=mpDecodeULWORD(l_p);
        l_p+=4;
        l_dataLeft-=4;
      } else {
        l_returnCode=0xFFFFFFFFL;
      }
      break;
    case MOLE_CMD_IDEN:
//      dbPrint("Wow, an IDEN packet! We know with whom we speak!");
      /* This is only used during login */
      if (l_dataLeft >= 4 ) {
        g_mpHostVersion=mpDecodeULWORD(l_p);
        l_p+=4;
        l_dataLeft-=4;
      } else {
        g_mpHostVersion=0L;
      }
      if (g_hoLoginWnd)
        PostMessage(g_hoLoginWnd,WM_USER_LOGIN_IDENTIFIED,0,0L);
      break;
    case MOLE_CMD_SYSD:
      /* This is only used during login - let's get out of login mode */
      unNewIden(l_p,l_dataLeft);
      g_hoConnectionState=HO_CONNECT_STATE_ONLINE;
      if (g_hoLoginWnd)
        PostMessage(g_hoLoginWnd,WM_USER_LOGIN_COMPLETE,0,0L);
      break;
    case MOLE_CMD_ALST:
//      dbPrint("Oh cool, an area listing!");
      unNewAreaList(l_p,l_dataLeft);
      break;
    case MOLE_CMD_WLST:
//      dbPrint("whoah! A world list!");
      unNewWorldList(l_p,l_dataLeft);
      break;
    case MOLE_CMD_MLST:
//      dbPrint("Hey, like, 69! A Mobile list!");
      unNewMobileList(l_p,l_dataLeft);
      break;
    case MOLE_CMD_OLST:
//      dbPrint("Party on! An Object list!");
      unNewObjectList(l_p,l_dataLeft);
      break;
    case MOLE_CMD_ADTL:
      unNewAreaDetail(l_p,l_dataLeft);
      break;
    case MOLE_CMD_WDTL:
//      dbPrint("Bazam! Worlds-o-rama.");
      unNewWorld(l_p,l_dataLeft);
      break;
    case MOLE_CMD_MDTL:
//      dbPrint("Oo-oo! a MOBILE!");
      unNewMobile(l_p,l_dataLeft);
      break;
    case MOLE_CMD_ODTL:
//      dbPrint("Objects R Us - new one!");
      unNewObject(l_p,l_dataLeft);
      break;
    case MOLE_CMD_RLST:
//      dbPrint("Reset list!!!!!");
      unNewResetList(l_p,l_dataLeft);
      break;
    case MOLE_CMD_PLST:
    case MOLE_CMD_RLRQ:
    case MOLE_CMD_MDRQ:
    case MOLE_CMD_MLRQ:
    case MOLE_CMD_OLRQ:
    case MOLE_CMD_WLRQ:
    case MOLE_CMD_ADRQ:
    case MOLE_CMD_ALRQ:
    case MOLE_CMD_PLRQ:
    case MOLE_CMD_IDRQ:
//    case MOLE_CMD_ECRQ: /* we don't support this in the client */
//    case MOLE_CMD_ECHO: /* we ignore these packets */
    case MOLE_CMD_MORE:
    default: /* unrecognized or unimplemented command */
      break;
  } /* switch */

  /* Notify our Edit Management facility that a packet has arrived */
  edPacketHasArrived(l_pktID,p_Cmd,l_returnCode);

  /* now remove request from the RequestQ - it's been answered */
  /* NOTE this is an important step - any requests which are AHEAD
   * of this are assumed to be UNANSWERED (you see, commands are
   * responded to sequentially, so if we skip one, we can assume
   * it's request was not answered or properly transmitted */
  /* First, remove this request from the queue */
  l_nextReq=l_req->rNext;
  mpReqExtract(l_req,&g_mpRequestQ);
  mpReqFree(l_req);
  /* now re-submit all requests found LATER in the linked list - note
     that new requests are appended to the FRONT of the list */
  while(l_nextReq) {
    l_req=l_nextReq;
    l_nextReq=l_req->rNext;
    /* extract this request from the queue */
    mpReqExtract(l_req,&g_mpRequestQ);
    /* and re-submit this request */
    mpReqSubmit(l_req,&g_mpRequestQ);
  }
  /* and we're done */
  return;
}

/* This proc monitors the request Q and, if there's a request which has been
 * waiting too long for a reply, it re-submits it. */
void mpCheckTimeout() {
  MPREQUEST *l_req,*l_nextreq;
  time_t l_time;
  char l_buf[100];
  int l_i;

//  dbPrint("Let's check up on our RequestQ for timeout errors");
  for (l_i=0,l_req=g_mpRequestQ;l_req;l_req=l_req->rNext,l_i++) {
    l_buf[0]=(char)(((l_req->rCmd)&0xFF000000L)>>24);
    l_buf[1]=(char)(((l_req->rCmd)&0x00FF0000L)>>16);
    l_buf[2]=(char)(((l_req->rCmd)&0x0000FF00L)>>8);
    l_buf[3]=(char)(((l_req->rCmd)&0x000000FFL));
    l_buf[4]=' ';
//    sprintf(&(l_buf[5]),"timestamp: %lu",(unsigned long)l_req->rTime);
//    dbPrint(l_buf);
  }
//  sprintf(l_buf,"requests queued: %i",l_i);
//  dbPrint(l_buf);


  /* work from oldest request back */
  if (!g_mpRequestQ) return; /* nothing to do */
  for (l_req=g_mpRequestQ;l_req->rNext;l_req=l_req->rNext);

  /* we should point to the last request now */
  l_time=time(NULL);
//  sprintf(l_buf,"current time= %lu",(unsigned long)l_time);
//  dbPrint(l_buf);
  do {
    l_nextreq=l_req->rPrevious;
    /* check the time stamp on this sucker. */
    if ((l_time-(g_tmTimerPeriod/500))>l_req->rTime) {
      /* oooooo, this one is toooo old. In our impatience, resubmit the request */
      /* extract this request from the queue */
//      dbPrint("Request Re-Queued");
      mpReqExtract(l_req,&g_mpRequestQ);
      /* and re-submit this request */
      mpReqSubmit(l_req,&g_mpRequestQ);
    }
    /* and go on to the next (er, I mean PREVIOUS) request */
    l_req=l_nextreq;
  } while (l_req);
  return;
}