Beispiel #1
0
void
CloudThermal::Save(Serialiser &s) const
{
  s.Write8(1);
  s.Write64(client_key);
  s << time;
  s.WriteT(Pack());
}
Beispiel #2
0
void Scene3700::Viewer::synchronise(Serialiser &s) {
	SceneObject::synchronise(s);
	s.syncAsByte(_active);
	s.syncAsSint16LE(_countdownCtr);
	for (int idx = 0; idx < 4; ++idx) {
		s.syncAsSint16LE(_frameList[idx]);
		s.syncAsSint16LE(_percentList[idx]);
	}
}
Beispiel #3
0
void
CloudData::Save(Serialiser &s) const
{
    s.Write32(CLOUD_MAGIC);
    s.Write32(CLOUD_VERSION);
    clients.Save(s);
    s.Write8(1);
    thermals.Save(s);
    s.Write8(0);
}
Beispiel #4
0
void
CloudThermalContainer::Save(Serialiser &s) const
{
  s.Write8(1);

  for (const auto &thermal : list) {
    s.Write8(1);
    thermal.Save(s);
  }

  s.Write8(0);
  s.Write8(0);
}
Beispiel #5
0
		RemoteRenderer(Network::Socket *sock)
			: m_Socket(sock)
		{
			map<RDCDriver,wstring> m = RenderDoc::Inst().GetReplayDrivers();

			m_Proxies.reserve(m.size());
			for(auto it=m.begin(); it != m.end(); ++it) m_Proxies.push_back(*it);
			
			{
				PacketType type;
				Serialiser *ser = NULL;
				GetPacket(type, &ser);

				m.clear();

				if(ser)
				{
					uint32_t count = 0;
					ser->Serialise("", count);

					for(uint32_t i=0; i < count; i++)
					{
						RDCDriver driver = RDC_Unknown;
						wstring name = L"";
						ser->Serialise("", driver);
						ser->Serialise("", name);

						m[driver] = name;
					}

					delete ser;
				}
			}

			m_RemoteDrivers.reserve(m.size());
			for(auto it=m.begin(); it != m.end(); ++it) m_RemoteDrivers.push_back(*it);

			m_ProxyDriver = NULL;
			m_ProxySerialiser = NULL;
		}
Beispiel #6
0
  TargetControl(Network::Socket *sock, string clientName, bool forceConnection, bool localhost)
      : m_Socket(sock), m_Local(localhost)
  {
    PacketType type;
    vector<byte> payload;

    m_PID = 0;

    {
      Serialiser ser("", Serialiser::WRITING, false);

      ser.SerialiseString("", clientName);
      ser.Serialise("", forceConnection);

      if(!SendPacket(m_Socket, ePacket_Handshake, ser))
      {
        SAFE_DELETE(m_Socket);
        return;
      }
    }

    Serialiser *ser = NULL;
    GetPacket(type, ser);

    // failed handshaking
    if(m_Socket == NULL || ser == NULL)
      return;

    RDCASSERT(type == ePacket_Handshake || type == ePacket_Busy);

    if(type == ePacket_Handshake)
    {
      ser->Serialise("", m_Target);
      ser->Serialise("", m_API);
      ser->Serialise("", m_PID);

      RDCLOG("Got remote handshake: %s (%s) [%u]", m_Target.c_str(), m_API.c_str(), m_PID);
    }
    else if(type == ePacket_Busy)
    {
      ser->Serialise("", m_Target);
      ser->Serialise("", m_API);
      ser->Serialise("", m_BusyClient);

      RDCLOG("Got remote busy signal: %s (%s) owned by %s", m_Target.c_str(), m_API.c_str(),
             m_BusyClient.c_str());
    }

    SAFE_DELETE(ser);
  }
Beispiel #7
0
		ReplayCreateStatus CreateProxyRenderer(uint32_t proxyid, const char *logfile, float *progress, ReplayRenderer **rend)
		{
			if(rend == NULL) return eReplayCreate_InternalError;

			if(proxyid >= m_Proxies.size())
			{
				RDCERR("Invalid proxy driver id %d specified for remote renderer", proxyid);
				return eReplayCreate_InternalError;
			}
			
			float dummy = 0.0f;
			if(progress == NULL)
				progress = &dummy;

			RDCDriver proxydrivertype = m_Proxies[proxyid].first;

			Serialiser ser("", Serialiser::WRITING, false);
		
			if(!SendChunkedFile(m_Socket, ePacket_CopyCapture, logfile, ser, progress))
			{
				SAFE_DELETE(m_Socket);
				return eReplayCreate_NetworkIOFailed;
			}

			RDCLOG("Sent file to replay host. Loading...");
			
			PacketType type = ePacket_Noop;
			while(m_Socket)
			{
				Serialiser *progressSer;
				GetPacket(type, &progressSer);

				if(!m_Socket || type != ePacket_LogOpenProgress) break;

				progressSer->Serialise("", *progress);

				RDCLOG("% 3.0f%%...", (*progress)*100.0f);
			}

			if(!m_Socket || type != ePacket_LogReady)
				return eReplayCreate_NetworkIOFailed;

			*progress = 1.0f;

			RDCLOG("Log ready on replay host");

			IReplayDriver *proxyDriver = NULL;
			auto status = RenderDoc::Inst().CreateReplayDriver(proxydrivertype, NULL, &proxyDriver);

			if(status != eReplayCreate_Success || !proxyDriver)
			{
				if(proxyDriver) proxyDriver->Shutdown();
				return status;
			}

			ReplayRenderer *ret = new ReplayRenderer();

			ProxySerialiser *proxy = new ProxySerialiser(m_Socket, proxyDriver);
			status = ret->SetDevice(proxy);

			if(status != eReplayCreate_Success)
			{
				SAFE_DELETE(ret);
				return status;
			}
			
			// ReplayRenderer takes ownership of the ProxySerialiser (as IReplayDriver)
			// and it cleans itself up in Shutdown.

			*rend = ret;

			return eReplayCreate_Success;
		}
Beispiel #8
0
  void ReceiveMessage(RemoteMessage *msg)
  {
    if(m_Socket == NULL)
    {
      msg->Type = eRemoteMsg_Disconnected;
      return;
    }

    if(!m_Socket->IsRecvDataWaiting())
    {
      if(!m_Socket->Connected())
      {
        SAFE_DELETE(m_Socket);
        msg->Type = eRemoteMsg_Disconnected;
      }
      else
      {
        Threading::Sleep(2);
        msg->Type = eRemoteMsg_Noop;
      }

      return;
    }

    PacketType type;
    Serialiser *ser = NULL;

    GetPacket(type, ser);

    if(m_Socket == NULL)
    {
      SAFE_DELETE(ser);

      msg->Type = eRemoteMsg_Disconnected;
      return;
    }
    else
    {
      if(type == ePacket_Noop)
      {
        SAFE_DELETE(ser);

        msg->Type = eRemoteMsg_Noop;
        return;
      }
      else if(type == ePacket_Busy)
      {
        string existingClient;
        ser->Serialise("", existingClient);

        SAFE_DELETE(ser);

        SAFE_DELETE(m_Socket);

        RDCLOG("Got busy signal: '%s", existingClient.c_str());
        msg->Type = eRemoteMsg_Busy;
        msg->Busy.ClientName = existingClient;
        return;
      }
      else if(type == ePacket_CopyCapture)
      {
        msg->Type = eRemoteMsg_CaptureCopied;

        ser->Serialise("", msg->NewCapture.ID);

        SAFE_DELETE(ser);

        msg->NewCapture.localpath = m_CaptureCopies[msg->NewCapture.ID];

        if(!RecvChunkedFile(m_Socket, ePacket_CopyCapture, msg->NewCapture.localpath.elems, ser, NULL))
        {
          SAFE_DELETE(ser);
          SAFE_DELETE(m_Socket);

          msg->Type = eRemoteMsg_Disconnected;
          return;
        }

        m_CaptureCopies.erase(msg->NewCapture.ID);

        SAFE_DELETE(ser);

        return;
      }
      else if(type == ePacket_NewChild)
      {
        msg->Type = eRemoteMsg_NewChild;

        ser->Serialise("", msg->NewChild.PID);
        ser->Serialise("", msg->NewChild.ident);

        RDCLOG("Got a new child process: %u %u", msg->NewChild.PID, msg->NewChild.ident);

        SAFE_DELETE(ser);

        return;
      }
      else if(type == ePacket_NewCapture)
      {
        msg->Type = eRemoteMsg_NewCapture;

        ser->Serialise("", msg->NewCapture.ID);
        ser->Serialise("", msg->NewCapture.timestamp);

        string path;
        ser->Serialise("", path);
        msg->NewCapture.localpath = path;

        if(!m_Local)
          msg->NewCapture.localpath = "";

        uint32_t thumblen = 0;
        ser->Serialise("", thumblen);

        create_array_uninit(msg->NewCapture.thumbnail, thumblen);

        size_t l = 0;
        byte *buf = &msg->NewCapture.thumbnail[0];
        ser->SerialiseBuffer("", buf, l);

        RDCLOG("Got a new capture: %d (time %llu) %d byte thumbnail", msg->NewCapture.ID,
               msg->NewCapture.timestamp, thumblen);

        SAFE_DELETE(ser);

        return;
      }
      else if(type == ePacket_RegisterAPI)
      {
        msg->Type = eRemoteMsg_RegisterAPI;

        ser->Serialise("", m_API);
        msg->RegisterAPI.APIName = m_API;

        RDCLOG("Used API: %s", m_API.c_str());

        SAFE_DELETE(ser);

        return;
      }
    }

    SAFE_DELETE(ser);

    msg->Type = eRemoteMsg_Noop;
  }
Beispiel #9
0
void RenderDoc::RemoteAccessClientThread(void *s)
{
  Threading::KeepModuleAlive();

  Network::Socket *client = (Network::Socket *)s;

  Serialiser ser("", Serialiser::WRITING, false);

  string api = "";
  RDCDriver driver;
  RenderDoc::Inst().GetCurrentDriver(driver, api);

  ser.Rewind();

  string target = RenderDoc::Inst().GetCurrentTarget();
  ser.Serialise("", target);
  ser.Serialise("", api);
  uint32_t mypid = Process::GetCurrentPID();
  ser.Serialise("", mypid);

  if(!SendPacket(client, ePacket_Handshake, ser))
  {
    SAFE_DELETE(client);

    {
      SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock);
      RenderDoc::Inst().m_SingleClientName = "";
    }

    Threading::ReleaseModuleExitThread();
    return;
  }

  const int pingtime = 1000;    // ping every 1000ms
  const int ticktime = 10;      // tick every 10ms
  int curtime = 0;

  vector<CaptureData> captures;
  vector<pair<uint32_t, uint32_t> > children;

  while(client)
  {
    if(RenderDoc::Inst().m_RemoteClientThreadShutdown || (client && !client->Connected()))
    {
      SAFE_DELETE(client);
      break;
    }

    ser.Rewind();

    Threading::Sleep(ticktime);
    curtime += ticktime;

    PacketType packetType = ePacket_Noop;

    string curapi;
    RenderDoc::Inst().GetCurrentDriver(driver, curapi);

    vector<CaptureData> caps = RenderDoc::Inst().GetCaptures();
    vector<pair<uint32_t, uint32_t> > childprocs = RenderDoc::Inst().GetChildProcesses();

    if(curapi != api)
    {
      api = curapi;

      ser.Serialise("", api);

      packetType = ePacket_RegisterAPI;
    }
    else if(caps.size() != captures.size())
    {
      uint32_t idx = (uint32_t)captures.size();

      captures.push_back(caps[idx]);

      packetType = ePacket_NewCapture;

      std::string path = FileIO::GetFullPathname(captures.back().path);

      ser.Serialise("", idx);
      ser.Serialise("", captures.back().timestamp);
      ser.Serialise("", path);

      uint32_t len = 0;
      RENDERDOC_GetThumbnail(captures.back().path.c_str(), NULL, len);
      byte *thumb = new byte[len];
      RENDERDOC_GetThumbnail(captures.back().path.c_str(), thumb, len);

      size_t l = len;
      ser.Serialise("", len);
      ser.SerialiseBuffer("", thumb, l);
      delete[] thumb;
    }
    else if(childprocs.size() != children.size())
    {
      uint32_t idx = (uint32_t)children.size();

      children.push_back(childprocs[idx]);

      packetType = ePacket_NewChild;

      ser.Serialise("", children.back().first);
      ser.Serialise("", children.back().second);
    }

    if(curtime < pingtime && packetType == ePacket_Noop)
    {
      if(client->IsRecvDataWaiting())
      {
        PacketType type;
        Serialiser *recvser = NULL;

        if(!RecvPacket(client, type, &recvser))
          SAFE_DELETE(client);

        if(client == NULL)
        {
          SAFE_DELETE(recvser);
          continue;
        }
        else if(type == ePacket_TriggerCapture)
        {
          RenderDoc::Inst().TriggerCapture();
        }
        else if(type == ePacket_QueueCapture)
        {
          uint32_t frameNum = 0;
          recvser->Serialise("", frameNum);

          RenderDoc::Inst().QueueCapture(frameNum);
        }
        else if(type == ePacket_CopyCapture)
        {
          caps = RenderDoc::Inst().GetCaptures();

          uint32_t id = 0;
          recvser->Serialise("", id);

          if(id < caps.size())
          {
            ser.Serialise("", id);

            if(!SendPacket(client, ePacket_CopyCapture, ser))
            {
              SAFE_DELETE(client);
              continue;
            }

            ser.Rewind();

            if(!SendChunkedFile(client, ePacket_CopyCapture, caps[id].path.c_str(), ser, NULL))
            {
              SAFE_DELETE(client);
              continue;
            }

            RenderDoc::Inst().MarkCaptureRetrieved(id);
          }
        }

        SAFE_DELETE(recvser);
      }

      continue;
    }

    curtime = 0;

    if(!SendPacket(client, packetType, ser))
    {
      SAFE_DELETE(client);
      continue;
    }
  }

  // give up our connection
  {
    SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock);
    RenderDoc::Inst().m_SingleClientName = "";
  }

  Threading::ReleaseModuleExitThread();
}
Beispiel #10
0
void RenderDoc::RemoteAccessServerThread(void *s)
{
  Threading::KeepModuleAlive();

  Network::Socket *sock = (Network::Socket *)s;

  RenderDoc::Inst().m_SingleClientName = "";

  Threading::ThreadHandle clientThread = 0;

  RenderDoc::Inst().m_RemoteClientThreadShutdown = false;

  while(!RenderDoc::Inst().m_RemoteServerThreadShutdown)
  {
    Network::Socket *client = sock->AcceptClient(false);

    if(client == NULL)
    {
      if(!sock->Connected())
      {
        RDCERR("Error in accept - shutting down server");

        SAFE_DELETE(sock);
        Threading::ReleaseModuleExitThread();
        return;
      }

      Threading::Sleep(5);

      continue;
    }

    string existingClient;
    string newClient;
    bool kick = false;

    // receive handshake from client and get its name
    {
      PacketType type;
      Serialiser *ser = NULL;
      if(!RecvPacket(client, type, &ser))
      {
        SAFE_DELETE(ser);
        SAFE_DELETE(client);
        continue;
      }

      if(type != ePacket_Handshake)
      {
        SAFE_DELETE(ser);
        SAFE_DELETE(client);
        continue;
      }

      ser->SerialiseString("", newClient);
      ser->Serialise("", kick);

      SAFE_DELETE(ser);

      if(newClient.empty())
      {
        SAFE_DELETE(client);
        continue;
      }
    }

    // see if we have a client
    {
      SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock);
      existingClient = RenderDoc::Inst().m_SingleClientName;
    }

    if(!existingClient.empty() && kick)
    {
      // forcibly close communication thread which will kill the connection
      RenderDoc::Inst().m_RemoteClientThreadShutdown = true;
      Threading::JoinThread(clientThread);
      Threading::CloseThread(clientThread);
      clientThread = 0;
      RenderDoc::Inst().m_RemoteClientThreadShutdown = false;
      existingClient = "";
    }

    if(existingClient.empty())
    {
      SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock);
      RenderDoc::Inst().m_SingleClientName = newClient;
    }

    // if we've claimed client status, spawn a thread to communicate
    if(existingClient.empty() || kick)
    {
      clientThread = Threading::CreateThread(RemoteAccessClientThread, client);
      continue;
    }
    else
    {
      // if we've been asked to kick the existing connection off
      // reject this connection and tell them who is busy
      Serialiser ser("", Serialiser::WRITING, false);

      string api = "";
      RDCDriver driver;
      RenderDoc::Inst().GetCurrentDriver(driver, api);

      string target = RenderDoc::Inst().GetCurrentTarget();
      ser.Serialise("", target);
      ser.Serialise("", api);

      ser.SerialiseString("", RenderDoc::Inst().m_SingleClientName);

      // don't care about errors, we're going to close the connection either way
      SendPacket(client, ePacket_Busy, ser);

      SAFE_DELETE(client);
    }
  }

  RenderDoc::Inst().m_RemoteClientThreadShutdown = true;
  // don't join, just close the thread, as we can't wait while in the middle of module unloading
  Threading::CloseThread(clientThread);
  clientThread = 0;

  Threading::ReleaseModuleExitThread();
}
Beispiel #11
0
void SoundManager::listenerSynchronise(Serialiser &s) {
	s.validate("SoundManager");
	warning("TODO: SoundManager listenerSynchronise");
}
Beispiel #12
0
void RenderDoc::RemoteAccessClientThread(void *s)
{
	Network::Socket *client = (Network::Socket *)s;

	Serialiser ser(L"", Serialiser::WRITING, false);

	wstring api = L"";
	RDCDriver driver;
	RenderDoc::Inst().GetCurrentDriver(driver, api);

	ser.Rewind();

	wstring target = RenderDoc::Inst().GetCurrentTarget();
	ser.Serialise("", target);
	ser.Serialise("", api);

	if(!SendPacket(client, ePacket_Handshake, ser))
	{
		SAFE_DELETE(client);

		{
			SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock);
			RenderDoc::Inst().m_SingleClientName = L"";
		}

		return;
	}

	const int pingtime = 1000; // ping every 1000ms
	const int ticktime = 10; // tick every 10ms
	int curtime = 0;

	vector<wstring> captures;

	while(client)
	{
		if(RenderDoc::Inst().m_RemoteClientThreadShutdown || (client && !client->Connected()))
		{
			SAFE_DELETE(client);
			break;
		}

		ser.Rewind();

		Threading::Sleep(ticktime);
		curtime += ticktime;

		PacketType packetType = ePacket_Noop;

		wstring curapi;
		RenderDoc::Inst().GetCurrentDriver(driver, curapi);

		if(curapi != api)
		{
			api = curapi;

			ser.Serialise("", api);

			packetType = ePacket_RegisterAPI;
		}
		else
		{
			vector<wstring> caps = RenderDoc::Inst().GetCaptures();

			if(caps.size() != captures.size())
			{
				uint32_t idx = (uint32_t)captures.size();

				captures.push_back(caps[idx]);

				packetType = ePacket_NewCapture;

				uint64_t timestamp = FileIO::GetModifiedTimestamp(captures.back().c_str());
				ser.Serialise("", idx);
				ser.Serialise("", timestamp);

				ser.Serialise("", captures.back());

				uint32_t len = 128*1024;
				byte *thumb = new byte[len];
				RENDERDOC_GetThumbnail(captures.back().c_str(), thumb, len);

				size_t l = len;
				ser.Serialise("", len);
				ser.SerialiseBuffer("", thumb, l);
				delete[] thumb;
			}
		}

		if(curtime < pingtime && packetType == ePacket_Noop)
		{
			if(client->IsRecvDataWaiting())
			{
				PacketType type;
				Serialiser *recvser = NULL;

				if(!RecvPacket(client, type, &recvser))
					SAFE_DELETE(client);

				if(client == NULL)
				{
					SAFE_DELETE(recvser);
					continue;
				}
				else if(type == ePacket_TriggerCapture)
				{
					RenderDoc::Inst().TriggerCapture();
				}
				else if(type == ePacket_QueueCapture)
				{
					uint32_t frameNum = 0;
					recvser->Serialise("", frameNum);
					
					RenderDoc::Inst().QueueCapture(frameNum);
				}
				else if(type == ePacket_CopyCapture)
				{
					vector<wstring> caps = RenderDoc::Inst().GetCaptures();

					uint32_t id = 0;
					recvser->Serialise("", id);

					if(id < caps.size())
					{
						ser.Serialise("", id);

						if(!SendPacket(client, ePacket_CopyCapture, ser))
						{
							SAFE_DELETE(client);
							continue;
						}

						ser.Rewind();

						if(!SendChunkedFile(client, ePacket_CopyCapture, caps[id].c_str(), ser, NULL))
						{
							SAFE_DELETE(client);
							continue;
						}

						RenderDoc::Inst().MarkCaptureRetrieved(id);
					}
				}

				SAFE_DELETE(recvser);
			}

			continue;
		}

		curtime = 0;

		if(!SendPacket(client, packetType, ser))
		{
			SAFE_DELETE(client);
			continue;
		}
	}
	
	// give up our connection
	{
		SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock);
		RenderDoc::Inst().m_SingleClientName = L"";
	}
}
Beispiel #13
0
void Compiler::scanStmt() {
    char buf[256]; // temporary buffer
    int t;
    
    switch(tok->getnext()){
    case T_IDENT: // it's an expression
    case T_INT:
    case T_FLOAT:
    case T_SUB:
    case T_BITNOT:
    case T_PLING:
    case T_STRING:
    case T_BACKTICK:
    case T_OPREN:
        // deal with debugging words!
        if(!strcmp(tok->getstring(),"dumplocs")){
            cg->emit(OP_SPECIAL,0);break;
        }
        if(!strcmp(tok->getstring(),"breakpoint")){
            cg->emit(OP_SPECIAL,1);break;
        }
        
        tok->rewind(); // put the token back
        // scan the expression, might be a label
        if(!scanExpr(true))
            // clear all statements if not a func or other oddity,
            // or just a dummy for recreation purposes if in immediate mode.
            cg->emit(cg->isCompiling()?OP_ENDESTMT:OP_ENDESTMT2);
        break;
    case T_LOAD:
        {
            if(cg->isCompiling())
                error("can only run 'load' in interactive mode");
            Session *s;
            if(tok->getnext()!=T_STRING)
                error("expected a string after 'load'");
            try {
                s = new Session(ses->api);
                s->feedFile(tok->getstring());
            } catch(Exception &e){
                delete s;
                throw e;
            }
            delete s;
        }
        break;
    case T_SAVE:
        {
            if(cg->isCompiling())
                error("can only run 'save' in interactive mode");
            if(tok->getnext()!=T_STRING)
                error("expected a string after 'save'");
            const char *fname = tok->getstring();
            
            FILE *a;
            if(!strlen(fname))
                a = stdout;
            else
                a = fopen(fname,"w");
            
            if(!a)
                error("cannot open file '%s'",fname);
            
            Serialiser *ser = new Serialiser(ses);
            ser->write(a);
            if(strlen(fname))
                fclose(a);
            delete ser;
        }
        break;
    case T_SAVEVAR:
        {
            if(cg->isCompiling())
                error("can only run 'savevar' in interactive mode");
            if(tok->getnext()!=T_IDENT)
                error("expected a variable name after 'savevar'");
            const char *vname = tok->getstring();
            int vdesc = lana->consts->findOrCreateString(vname);
            if(tok->getnext()!=T_STRING)
                error("expected a string after 'savevar'");
            const char *fname = tok->getstring();
            
            // try to get the value
            Value *v;
            int id;
            id = lana->globs->find(vdesc);
            if(id>=0) {
                v = lana->globs->get(id); // it's a global
            } else {
                id = ses->findSesVar(vdesc);
                if(id<0)
                    error("variable not found: %s",lana->consts->getStr(vdesc));
                v = ses->getSesVar(id);
            }
            
            FILE *a;
            if(!strlen(fname))
                a = stdout;
            else
                a = fopen(fname,"w");
            
            if(!a)
                error("cannot open file '%s'",fname);
            
            Serialiser *ser = new Serialiser(ses);
            ser->serialiseValue(a,v,lana->consts->getStr(vdesc));
            if(strlen(fname))
                fclose(a);
            delete ser;
        }
        break;
    case T_FOR:
        scanFor();
        break;
    case T_ENDFOR:
        scanEndFor();
        break;
    case T_THIS:
        tok->rewind(); // put the token back
        if(!scanExpr(true))
            // clear all statements if not a func or other oddity,
            // or just a dummy for recreation purposes if in immediate mode.
            cg->emit(cg->isCompiling()?OP_ENDESTMT:OP_ENDESTMT2);
        break;
    case T_GOTO:
        if(!cg->isCompiling())
            error("must be compiling a function/procedure to use '%s'",tok->getstring());
        scanGoto();
        break;
    case T_ENDFUNC:
        scanEndFunc();
        break;
    case T_END:
        if(!(lana->opFlags & LOP_STRIPCOMMENTS))
            cg->emit(OP_BLANKLINE); // yes, these are wasteful .. very slightly
        break;
    case T_IF:
        // first we push a special value onto the compiler stack
        // to mark the start of this if..elseif..elseif..endif
        cg->current->cpush(-9999);
        // we scan the expression 
        if(scanExpr())
            error("cannot use a function/procedure expression in if");
        // stack and output an incomplete if - but this might be a normal if, or a quick if.
        cg->current->cpushhere();
        
        // now for some cleverness. Is the next token a colon?
        if(tok->getnext() == T_COLON){
            cg->emit(OP_QUICKIF,-100);
            // if so, parse the next statement recursively
            scanStmt();
            // note that we don't need to output a quick endif, since the recreator
            // doesn't need it!
            instruction *ptr = cg->current->cpoplocandcheck(OP_QUICKIF,OP_QUICKIF); // MUST be an OP_IF, no ELSE.
            if(!ptr)
                error("not a simple statement in quick-if");
            // write the IF, ELSE or ELSEIF again with the correct distance
            *ptr = INST(INSTOP(*ptr),cg->current->getdiff(ptr));
            // now pop off!
            int n;
            do{
                n = cg->current->cpop();
            }while(n!=-9999);
        } else {
            // not - put it back!
            tok->rewind();
            cg->emit(OP_IF,-100);
        }
        break;
    case T_ENDIF:
        {
            // get the corresponding OP_IF, OP_ELSEIF or OP_ELSE
            cg->emit(OP_ENDIF);
            instruction *ptr = cg->current->cpoplocandcheck(OP_IF,OP_ELSE);
            if(!ptr)
                error("mismatched endif");
            // write the IF, ELSE or ELSEIF again with the correct distance
            *ptr = INST(INSTOP(*ptr),cg->current->getdiff(ptr));
            
            // now pop and fixup OP_JMPELSEIFs until we get the special -9999 which marked the start
            
            for(;;){
                int n = cg->current->cpop();
                if(n==-9999)break; // we're done!
                // we're not done - get the code pointer
                instruction *ptr = cg->current->getPtr(n*sizeof(instruction));
                // make sure it's a OP_JMPELSEIF!
                if(INSTOP(*ptr)!=OP_JMPELSEIF)
                    error("badly formed conditional statement");
                // change it so that it jumps to the current location
                *ptr = INST(INSTOP(*ptr),cg->current->getdiff(ptr));
            }
        }
        break;
    case T_ELSEIF:
        {
            // pop the instruction off the stack, an OP_IF or OP_ELSEIF
            instruction *ptr = cg->current->cpoplocandcheck(OP_IF,OP_ELSEIF);
            // first we need to terminate the previous condition, so
            // push the location and output a OP_JMPELSEIF ready to fill in.
            // This will get left on the stack!
            cg->current->cpushhere();
            cg->emit(OP_JMPELSEIF,-100);
            // now make the IF or ELSEIF we popped jump to this point
            *ptr = INST(INSTOP(*ptr),cg->current->getdiff(ptr));
            
            // now scan the expression
            if(scanExpr())
                error("cannot use a function/procedure expression in if");
            // push the location, and..
            // output an OP_ELSEIF with a dummy jump
            cg->current->cpushhere();
            cg->emit(OP_ELSEIF,-100);
        }
        break;
    case T_ELSE:
        {
            // write the OP_ELSE which will become a jump forward,
            // but first recording the location
            int elseloc = cg->current->getloc();
            cg->emit(OP_ELSE,-100);
            // now we need to make the IF jump to here
            // get the corresponding OP_IF or OP_ELSEIF
            instruction *ptr = cg->current->cpoplocandcheck(OP_IF,OP_ELSEIF);
            if(!ptr)
                error("mismatched else");
            // write the IF again with the correct jump distance
            int diff = cg->current->getdiff(ptr);
            *ptr = INST(INSTOP(*ptr),diff);
            // now push the location of the OP_ELSE, which
            // will get processed by the OP_ENDIF
            cg->current->cpush(elseloc);
        }
        break;
        // pop the location
    case T_RETURN:
        if(!cg->isCompiling())
            error("must be compiling a function/procedure to use '%s'",tok->getstring());
        if(tok->getnext() == T_END){ // end of line?
            tok->rewind();
            // no return value
            if(cg->current->ldth.flags & LDTF_RETURNS)
                error("functions must return a value");
            cg->emit(OP_RETURN,0);
        } else {
            tok->rewind();
            if(!(cg->current->ldth.flags & LDTF_RETURNS))
                error("procedures cannot return a value");
            if(scanExpr())
                error("cannot directly return a function");
            cg->emit(OP_RETURN,1);
        }
        break;
    case T_WHILE:
        if(!cg->isCompiling())
            error("must be compiling a function/procedure to use '%s'",tok->getstring());
        // push the current location onto the stack - this is where ENDWHILE will
        // jump to
        cg->current->cpushhere();
        // we also create and push the loop data here, so that we can use break and
        // continue!
        cg->current->newloop();
        // scan and output the expression
        if(scanExpr())
            error("cannot use a function/procedure expression in `while`");
        // push the WHILE onto the stack so we can write the terminating jump into it
        cg->current->cpushhere();
        // output OP_WHILE with a dummy
        cg->emit(OP_WHILE,-100);
        break;
    case T_ENDWHILE:
        {
            // pop the location of the WHILE from the stack
            instruction *whileptr = cg->current->cpoplocandcheck(OP_WHILE,OP_WHILE);
            if(!whileptr)
                error("mismatched endwhile");
            // pop the location for the backward jump
            instruction *jumpdest = cg->current->cpoplocation();
            // output the endwhile, which will do the backward jump
            cg->emit(OP_ENDWHILE,cg->current->getdiff(jumpdest));
            // now patch the while instruction with the forward jump to use if the
            // condition is false
            *whileptr = INST(OP_WHILE,cg->current->getdiff(whileptr));
            /// and end the loop, setting the break label and popping the loop stack
            cg->current->endloop();
            break;
        }
        
    case T_REPEAT:
        if(!cg->isCompiling())
            error("must be compiling a function/procedure to use '%s'",tok->getstring());
        // output OP_REPEAT, pushing its location. We don't
        // jump to here, though - we jump to the following opcode.
        // This is done just so we can check that the until matches
        // a repeat. See T_UNTIL.
        cg->current->cpushhere();
        cg->current->newloop(); // push and initialise a new loop stack entry (see T_WHILE above)
        cg->emit(OP_REPEAT,0);
        break;
    case T_UNTIL:
        {
            // scan and output the expression
            if(scanExpr())
                error("cannot use a function/procedure expression in `until`");
            // pop the location of the OP_REPEAT from the stack
            instruction *ptr = cg->current->cpoplocandcheck(OP_REPEAT,OP_REPEAT);
            if(!ptr)
                error("mismatched `until'");
            // increment this, because we want to save cycles by
            // jumping past the OP_REPEAT (which is a kind of noop)
            ptr++;
            // and output the OP_UNTIL jump
            cg->emit(OP_UNTIL,cg->current->getdiff(ptr));
            cg->current->endloop(); // end the current loop stack entry (see T_ENDWHILE above)
            break;
        }
    case T_BREAK:
        {
            // we want to break out of the topmost loop on the loop stack
            
            // get address we're about to write to
            instruction *op = cg->current->getlocptr();
            // output the break which will be patched later
            cg->emit(OP_BREAK,-100);
            // and this jump as a jump to be patched when the break label is resolved
            LoopData *d = cg->current->loopstack.peekptr();
            if(!d)
                throw ParseException("break with no loop");
            d->breaklabel.jumpFrom(op);
        }
        break;
    case T_CONTINUE:
        {
            // we want to terminate the current iteration of the topmost loop on the loop stack
            // and immediately start the loop code again
            // get address we're about to write to
            instruction *op = cg->current->getlocptr();
            // output the break which will be patched later
            cg->emit(OP_CONTINUE,-100);
            // and this jump as a jump to be patched when the break label is resolved
            LoopData *d = cg->current->loopstack.peekptr();
            if(!d)
                throw ParseException("continue with no loop");
            d->continuelabel.jumpFrom(op);            
        }
        break;
    case T_COMMENT:
        scanComment(true);
        break;
    default:
        error("unexpected token '%s'",tok->getstring());
    }
    
    // see if there's a comment at the end
    scanPossibleComment();
    
    if(tok->getnext()!=T_END)
        error("trailing garbage at end of line");
}