<commit_before>/* bzflag
 * Copyright (c) 1993-2011 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "GameKeeper.h"

/* system headers */
#include <vector>
#include <string>

/* common headers */
#include "GameTime.h"

GameKeeper::Player *GameKeeper::Player::playerList[PlayerSlot] = {NULL};
bool GameKeeper::Player::allNeedHostbanChecked = false;

void *PackPlayerInfo(void *buf, int playerIndex, uint8_t properties )
{
  buf = nboPackUByte(buf, playerIndex);
  buf = nboPackUByte(buf, properties);
  return buf;
}

GameKeeper::Player::Player(int _playerIndex,
			   const struct sockaddr_in &clientAddr, int fd,
			   tcpCallback _clientCallback):
  player(_playerIndex), lagInfo(&player),
  playerIndex(_playerIndex), closed(false), clientCallback(_clientCallback),
  needThisHostbanChecked(false), idFlag(-1)
{
  playerList[playerIndex] = this;

  lastState.order  = 0;
  // Timestamp 0.0 -> not yet available
  stateTimeStamp   = 0.0f;
  gameTimeRate = GameTime::startRate;
  gameTimeNext = TimeKeeper::getCurrent();
  netHandler       = new NetHandler(&player, clientAddr, playerIndex, fd);
  _LSAState = start;
  bzIdentifier = "";
  isParting = false;
  playerHandler = NULL;
  score.playerID = _playerIndex;
}

GameKeeper::Player::Player(int _playerIndex,
			   NetHandler *handler,
			   tcpCallback _clientCallback):
  player(_playerIndex), lagInfo(&player),
  playerIndex(_playerIndex), closed(false), clientCallback(_clientCallback),
  needThisHostbanChecked(false)
{
  playerList[playerIndex] = this;

  lastState.order  = 0;
  // Timestamp 0.0 -> not yet available
  stateTimeStamp   = 0.0f;
  gameTimeRate = GameTime::startRate;
  gameTimeNext = TimeKeeper::getCurrent();
  netHandler = handler;
  _LSAState = start;
  bzIdentifier = "";
  isParting = false;
  netHandler->setPlayer((PlayerInfo*)this,_playerIndex);
  playerHandler = NULL;
  score.playerID = _playerIndex;
}

GameKeeper::Player::Player(int _playerIndex, bz_ServerSidePlayerHandler *handler)
  : player(_playerIndex), lagInfo(&player),
  playerIndex(_playerIndex), closed(false), clientCallback(NULL),
  needThisHostbanChecked(false)
{
  playerList[playerIndex] = this;

  lastState.order  = 0;
  // Timestamp 0.0 -> not yet available
  stateTimeStamp   = 0.0f;
  gameTimeRate = GameTime::startRate;
  gameTimeNext = TimeKeeper::getCurrent();
  netHandler = NULL;
  _LSAState = start;
  bzIdentifier = "";
  isParting = false;
  netHandler->setPlayer((PlayerInfo*)this,_playerIndex);
  playerHandler = handler;
  score.playerID = _playerIndex;
}

GameKeeper::Player::~Player()
{
  flagHistory.clear();
  delete netHandler;
  playerList[playerIndex] = NULL;
}

int GameKeeper::Player::count()
{
  Player *playerData;
  int     count = 0;

  for (int i = 0; i < PlayerSlot; i++)
    if ((playerData = playerList[i]) && !playerData->closed
	&& playerData->player.isPlaying())
      count++;
  return count;
}

void GameKeeper::Player::updateLatency(float &waitTime)
{
  Player* playerData;
  int p;

  for (p = 0; p < PlayerSlot; p++) {
    if ((playerData = playerList[p]) && !playerData->closed) {
      // get time for next lagping
      playerData->lagInfo.updateLatency(waitTime);
    }
  }
}

void GameKeeper::Player::dumpScore()
{
  Player *playerData;

  std::cout << "\n#players\n";
  int p;
  for (p = 0; p < PlayerSlot; p++)
    if ((playerData = playerList[p]) && !playerData->closed
	&& playerData->player.isPlaying()) {
      playerData->score.dump();
      std::cout << ' ' << playerData->player.getCallSign() << std::endl;
    }
}

int GameKeeper::Player::anointRabbit(int oldRabbit)
{
  float topRatio    = -100000.0f;
  int   rabbitIndex = NoPlayer;

  Player *playerData;
  int     i;
  bool    goodRabbitSelected = false;

  for (i = 0; i < PlayerSlot; i++)
    if ((playerData = playerList[i]) && !playerData->closed
	&& playerData->player.canBeRabbit(true)) {
	bool  goodRabbit = i != oldRabbit && playerData->player.isAlive();
	float ratio      = playerData->score.ranking();
	bool  select     = false;
	if (goodRabbitSelected) {
	  if (goodRabbit && (ratio > topRatio)) {
	    select = true;
	  }
	} else {
	  if (goodRabbit) {
	    select	     = true;
	    goodRabbitSelected = true;
	  } else {
	    if (ratio > topRatio)
	      select = true;
	  }
	}
	if (select) {
	  topRatio = ratio;
	  rabbitIndex = i;
	}
      }
  return rabbitIndex;
}

void GameKeeper::Player::updateNextGameTime()
{
  if (gameTimeRate < GameTime::startRate) {
    gameTimeRate = GameTime::startRate;
  } else if (gameTimeRate < GameTime::finalRate) {
    gameTimeRate = gameTimeRate * 1.25f;
  } else {
    gameTimeRate = GameTime::finalRate;
  }
  gameTimeNext = TimeKeeper::getCurrent();
  gameTimeNext += gameTimeRate;
  return;
}

void *GameKeeper::Player::packAdminInfo(void *buf)
{
  buf = nboPackUByte(buf, netHandler->sizeOfIP());
  buf = nboPackUByte(buf, playerIndex);
  buf = netHandler->packAdminInfo(buf);
  return buf;
}

void *GameKeeper::Player::packPlayerInfo(void *buf)
{
  buf = PackPlayerInfo(buf, playerIndex, accessInfo.getPlayerProperties());
  return buf;
}

void *GameKeeper::Player::packPlayerUpdate(void *buf)
{
  buf = nboPackUByte(buf, playerIndex);
  buf = player.packUpdate(buf);
  buf = score.pack(buf);
  buf = player.packId(buf);
  return buf;
}

void GameKeeper::Player::setPlayerAddMessage ( PlayerAddMessage &msg )
{
	msg.playerID = playerIndex;
	msg.team = player.getTeam();
	msg.type = player.getType();
	msg.wins = score.getWins();
	msg.losses = score.getLosses();
	msg.tks = score.getTKs();
	msg.callsign =  player.getCallSign();
	msg.motto =  player.getMotto();
}

std::vector<int> GameKeeper::Player::allowed(PlayerAccessInfo::AccessPerm right,
					     int targetPlayer)
{
  std::vector<int> receivers;
  Player* playerData;

  if (targetPlayer != -1) {
    if ((playerData = playerList[targetPlayer]) && !playerData->closed
	&& playerData->accessInfo.hasPerm(right))
      receivers.push_back(targetPlayer);
  } else {
    for (int i = 0; i < PlayerSlot; i++)
      if ((playerData = playerList[i]) && !playerData->closed
	  && playerData->accessInfo.hasPerm(right))
	receivers.push_back(i);
  }

  return receivers;
}

bool GameKeeper::Player::loadEnterData(uint16_t &rejectCode,
				       char *rejectMsg)
{
  // look if there is as name clash, we won't allow this
  for (int i = 0; i < PlayerSlot; i++) {
    Player *otherData = playerList[i];
    if (i == playerIndex || !otherData || !otherData->player.isPlaying())
      continue;
    if (otherData->closed)
      continue;
    if (!strcasecmp(otherData->player.getCallSign(), player.getCallSign())) {
      rejectCode   = RejectRepeatCallsign;
      strcpy(rejectMsg, "The callsign specified is already in use.");
      return false;
    }
  }

  return true;
}

void GameKeeper::Player::signingOn(bool ctf)
{
  player.resetPlayer(ctf);
  player.signingOn();
  lagInfo.reset();
}

// Attempt to retrive a slot number for a player specified as EITHER "callsign" or "#<slot>"
int GameKeeper::Player::getPlayerIDByName(const std::string &name)
{
  Player* playerData;
  int slot = -1; // invalid

  if (sscanf (name.c_str(), "#%d", &slot) == 1) {
    if ( ! GameKeeper::Player::getPlayerByIndex(slot) )
      return -1;
    return slot;
  } else {
    for (int i = 0; i < PlayerSlot; i++)
      if ((playerData = playerList[i]) && !playerData->closed
	&& (TextUtils::compare_nocase(playerData->player.getCallSign(), name) == 0))
	return i;
  }
  return -1;
}

void GameKeeper::Player::reloadAccessDatabase()
{
  Player* playerData;
  for (int i = 0; i < PlayerSlot; i++)
    if ((playerData = playerList[i]) && !playerData->closed)
      playerData->accessInfo.reloadInfo();
}

void GameKeeper::Player::close()
{
  closed = true;
}

bool GameKeeper::Player::clean()
{
  Player* playerData;
  // Trying to detect if this action cleaned the array of player
  bool empty    = true;
  bool ICleaned = false;
  for (int i = 0; i < PlayerSlot; i++)
    if ((playerData = playerList[i])) {
      if (playerData->closed) {
	delete playerData;
	ICleaned = true;
      } else {
	empty = false;
      }
    }
  return empty && ICleaned;
}

int GameKeeper::Player::getFreeIndex(int min, int max)
{
  for (int i = min; i < max; i++)
    if (!playerList[i])
      return i;
  return max;
}

void GameKeeper::Player::handleTcpPacket(fd_set *set)
{
  if (netHandler->isFdSet(set)) {
    const RxStatus e = netHandler->tcpReceive();
    if (e == ReadPart)
      return;
    clientCallback(*netHandler, playerIndex, e);
  }
}

void GameKeeper::Player::setPlayerState(float pos[3], float azimuth)
{
  serverTimeStamp = (float)TimeKeeper::getCurrent().getSeconds();
  memcpy(lastState.pos, pos, sizeof(float) * 3);
  lastState.azimuth = azimuth;
  // Set Speeds to 0 too
  memset(lastState.velocity, 0, sizeof(float) * 3);
  lastState.angVel = 0.0f;
  stateTimeStamp   = 0.0f;

  // player is alive.
  player.setAlive();

}

int GameKeeper::Player::maxShots = 0;
void GameKeeper::Player::setMaxShots(int _maxShots)
{
  maxShots = _maxShots;
}

bool GameKeeper::Player::addShot(int id, int salt, FiringInfo &firingInfo)
{
  float now = (float)TimeKeeper::getCurrent().getSeconds();
  if (id < (int)shotsInfo.size() && shotsInfo[id].present
      && now < shotsInfo[id].expireTime) {
    logDebugMessage(2,"Player %s [%d] shot id %d duplicated\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  // verify shot number
  if (id > maxShots - 1) {
    logDebugMessage(2,"Player %s [%d] shot id %d out of range %d\n",
	  player.getCallSign(), playerIndex, id, maxShots);
    return false;
  }

  shotsInfo.resize(maxShots);

  float lifeTime = BZDB.eval(StateDatabase::BZDB_RELOADTIME);
  if (firingInfo.flagType == Flags::RapidFire)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_RFIREADLIFE);
  else if (firingInfo.flagType == Flags::MachineGun)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_MGUNADLIFE);
  else if (firingInfo.flagType == Flags::GuidedMissile)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_GMADLIFE) + .01f;
  else if (firingInfo.flagType == Flags::Laser)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_LASERADLIFE);
  else if (firingInfo.flagType == Flags::ShockWave)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_SHOCKADLIFE);
  else if (firingInfo.flagType == Flags::Thief)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_THIEFADLIFE);

  ShotInfo myShot;
  myShot.firingInfo  = firingInfo;
  myShot.salt	= salt;
  myShot.expireTime  = now + lifeTime;
  myShot.present     = true;
  myShot.running     = true;

  shotsInfo[id] = myShot;
  return true;
}

bool GameKeeper::Player::removeShot(int id, int salt)
{
  float now = (float)TimeKeeper::getCurrent().getSeconds();
  if (id >= (int)shotsInfo.size() || !shotsInfo[id].present
      || now >= shotsInfo[id].expireTime) {
    logDebugMessage(2,"Player %s [%d] trying to stop the unexistent shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  if (shotsInfo[id].salt != salt) {
    logDebugMessage(2,"Player %s [%d] trying to stop a mismatched shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  if (!shotsInfo[id].running)
    return false;
  shotsInfo[id].running = false;
  return true;
}

bool GameKeeper::Player::updateShot(int id, int salt)
{
  float now = (float)TimeKeeper::getCurrent().getSeconds();
  if (id >= (int)shotsInfo.size() || !shotsInfo[id].present
      || now >= shotsInfo[id].expireTime) {
    logDebugMessage(2,"Player %s [%d] trying to update an unexistent shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  if (shotsInfo[id].salt != salt) {
    logDebugMessage(2,"Player %s [%d] trying to update a mismatched shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  if (!shotsInfo[id].running)
    return false;
  // only GM can be updated
  if (shotsInfo[id].firingInfo.flagType != Flags::GuidedMissile) {
    logDebugMessage(2,"Player %s [%d] trying to update a non GM shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  return true;
}

void GameKeeper::Player::setPlayerState(PlayerState state, float timestamp)
{
  lagInfo.updateLag(timestamp, state.order - lastState.order > 1);
  player.updateIdleTime();
  lastState      = state;
  stateTimeStamp = timestamp;
  serverTimeStamp = (float)TimeKeeper::getCurrent().getSeconds();
}

void GameKeeper::Player::getPlayerState(float pos[3], float &azimuth)
{
  memcpy(pos, lastState.pos, sizeof(float) * 3);
  azimuth = lastState.azimuth;
}

void GameKeeper::Player::setLastIdFlag(int _idFlag) {
  idFlag = _idFlag;
}

int GameKeeper::Player::getLastIdFlag() {
  return idFlag;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>don't set the NetHandler player when we don't have a NetHandler (server side bots)<commit_after>/* bzflag
 * Copyright (c) 1993-2011 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "GameKeeper.h"

/* system headers */
#include <vector>
#include <string>

/* common headers */
#include "GameTime.h"

GameKeeper::Player *GameKeeper::Player::playerList[PlayerSlot] = {NULL};
bool GameKeeper::Player::allNeedHostbanChecked = false;

void *PackPlayerInfo(void *buf, int playerIndex, uint8_t properties )
{
  buf = nboPackUByte(buf, playerIndex);
  buf = nboPackUByte(buf, properties);
  return buf;
}

GameKeeper::Player::Player(int _playerIndex,
			   const struct sockaddr_in &clientAddr, int fd,
			   tcpCallback _clientCallback):
  player(_playerIndex), lagInfo(&player),
  playerIndex(_playerIndex), closed(false), clientCallback(_clientCallback),
  needThisHostbanChecked(false), idFlag(-1)
{
  playerList[playerIndex] = this;

  lastState.order  = 0;
  // Timestamp 0.0 -> not yet available
  stateTimeStamp   = 0.0f;
  gameTimeRate = GameTime::startRate;
  gameTimeNext = TimeKeeper::getCurrent();
  netHandler       = new NetHandler(&player, clientAddr, playerIndex, fd);
  _LSAState = start;
  bzIdentifier = "";
  isParting = false;
  playerHandler = NULL;
  score.playerID = _playerIndex;
}

GameKeeper::Player::Player(int _playerIndex,
			   NetHandler *handler,
			   tcpCallback _clientCallback):
  player(_playerIndex), lagInfo(&player),
  playerIndex(_playerIndex), closed(false), clientCallback(_clientCallback),
  needThisHostbanChecked(false)
{
  playerList[playerIndex] = this;

  lastState.order  = 0;
  // Timestamp 0.0 -> not yet available
  stateTimeStamp   = 0.0f;
  gameTimeRate = GameTime::startRate;
  gameTimeNext = TimeKeeper::getCurrent();
  netHandler = handler;
  _LSAState = start;
  bzIdentifier = "";
  isParting = false;
  netHandler->setPlayer((PlayerInfo*)this,_playerIndex);
  playerHandler = NULL;
  score.playerID = _playerIndex;
}

GameKeeper::Player::Player(int _playerIndex, bz_ServerSidePlayerHandler *handler)
  : player(_playerIndex), lagInfo(&player),
  playerIndex(_playerIndex), closed(false), clientCallback(NULL),
  needThisHostbanChecked(false)
{
  playerList[playerIndex] = this;

  lastState.order  = 0;
  // Timestamp 0.0 -> not yet available
  stateTimeStamp   = 0.0f;
  gameTimeRate = GameTime::startRate;
  gameTimeNext = TimeKeeper::getCurrent();
  netHandler = NULL;
  _LSAState = start;
  bzIdentifier = "";
  isParting = false;
  playerHandler = handler;
  score.playerID = _playerIndex;
}

GameKeeper::Player::~Player()
{
  flagHistory.clear();
  delete netHandler;
  playerList[playerIndex] = NULL;
}

int GameKeeper::Player::count()
{
  Player *playerData;
  int     count = 0;

  for (int i = 0; i < PlayerSlot; i++)
    if ((playerData = playerList[i]) && !playerData->closed
	&& playerData->player.isPlaying())
      count++;
  return count;
}

void GameKeeper::Player::updateLatency(float &waitTime)
{
  Player* playerData;
  int p;

  for (p = 0; p < PlayerSlot; p++) {
    if ((playerData = playerList[p]) && !playerData->closed) {
      // get time for next lagping
      playerData->lagInfo.updateLatency(waitTime);
    }
  }
}

void GameKeeper::Player::dumpScore()
{
  Player *playerData;

  std::cout << "\n#players\n";
  int p;
  for (p = 0; p < PlayerSlot; p++)
    if ((playerData = playerList[p]) && !playerData->closed
	&& playerData->player.isPlaying()) {
      playerData->score.dump();
      std::cout << ' ' << playerData->player.getCallSign() << std::endl;
    }
}

int GameKeeper::Player::anointRabbit(int oldRabbit)
{
  float topRatio    = -100000.0f;
  int   rabbitIndex = NoPlayer;

  Player *playerData;
  int     i;
  bool    goodRabbitSelected = false;

  for (i = 0; i < PlayerSlot; i++)
    if ((playerData = playerList[i]) && !playerData->closed
	&& playerData->player.canBeRabbit(true)) {
	bool  goodRabbit = i != oldRabbit && playerData->player.isAlive();
	float ratio      = playerData->score.ranking();
	bool  select     = false;
	if (goodRabbitSelected) {
	  if (goodRabbit && (ratio > topRatio)) {
	    select = true;
	  }
	} else {
	  if (goodRabbit) {
	    select	     = true;
	    goodRabbitSelected = true;
	  } else {
	    if (ratio > topRatio)
	      select = true;
	  }
	}
	if (select) {
	  topRatio = ratio;
	  rabbitIndex = i;
	}
      }
  return rabbitIndex;
}

void GameKeeper::Player::updateNextGameTime()
{
  if (gameTimeRate < GameTime::startRate) {
    gameTimeRate = GameTime::startRate;
  } else if (gameTimeRate < GameTime::finalRate) {
    gameTimeRate = gameTimeRate * 1.25f;
  } else {
    gameTimeRate = GameTime::finalRate;
  }
  gameTimeNext = TimeKeeper::getCurrent();
  gameTimeNext += gameTimeRate;
  return;
}

void *GameKeeper::Player::packAdminInfo(void *buf)
{
  buf = nboPackUByte(buf, netHandler->sizeOfIP());
  buf = nboPackUByte(buf, playerIndex);
  buf = netHandler->packAdminInfo(buf);
  return buf;
}

void *GameKeeper::Player::packPlayerInfo(void *buf)
{
  buf = PackPlayerInfo(buf, playerIndex, accessInfo.getPlayerProperties());
  return buf;
}

void *GameKeeper::Player::packPlayerUpdate(void *buf)
{
  buf = nboPackUByte(buf, playerIndex);
  buf = player.packUpdate(buf);
  buf = score.pack(buf);
  buf = player.packId(buf);
  return buf;
}

void GameKeeper::Player::setPlayerAddMessage ( PlayerAddMessage &msg )
{
	msg.playerID = playerIndex;
	msg.team = player.getTeam();
	msg.type = player.getType();
	msg.wins = score.getWins();
	msg.losses = score.getLosses();
	msg.tks = score.getTKs();
	msg.callsign =  player.getCallSign();
	msg.motto =  player.getMotto();
}

std::vector<int> GameKeeper::Player::allowed(PlayerAccessInfo::AccessPerm right,
					     int targetPlayer)
{
  std::vector<int> receivers;
  Player* playerData;

  if (targetPlayer != -1) {
    if ((playerData = playerList[targetPlayer]) && !playerData->closed
	&& playerData->accessInfo.hasPerm(right))
      receivers.push_back(targetPlayer);
  } else {
    for (int i = 0; i < PlayerSlot; i++)
      if ((playerData = playerList[i]) && !playerData->closed
	  && playerData->accessInfo.hasPerm(right))
	receivers.push_back(i);
  }

  return receivers;
}

bool GameKeeper::Player::loadEnterData(uint16_t &rejectCode,
				       char *rejectMsg)
{
  // look if there is as name clash, we won't allow this
  for (int i = 0; i < PlayerSlot; i++) {
    Player *otherData = playerList[i];
    if (i == playerIndex || !otherData || !otherData->player.isPlaying())
      continue;
    if (otherData->closed)
      continue;
    if (!strcasecmp(otherData->player.getCallSign(), player.getCallSign())) {
      rejectCode   = RejectRepeatCallsign;
      strcpy(rejectMsg, "The callsign specified is already in use.");
      return false;
    }
  }

  return true;
}

void GameKeeper::Player::signingOn(bool ctf)
{
  player.resetPlayer(ctf);
  player.signingOn();
  lagInfo.reset();
}

// Attempt to retrive a slot number for a player specified as EITHER "callsign" or "#<slot>"
int GameKeeper::Player::getPlayerIDByName(const std::string &name)
{
  Player* playerData;
  int slot = -1; // invalid

  if (sscanf (name.c_str(), "#%d", &slot) == 1) {
    if ( ! GameKeeper::Player::getPlayerByIndex(slot) )
      return -1;
    return slot;
  } else {
    for (int i = 0; i < PlayerSlot; i++)
      if ((playerData = playerList[i]) && !playerData->closed
	&& (TextUtils::compare_nocase(playerData->player.getCallSign(), name) == 0))
	return i;
  }
  return -1;
}

void GameKeeper::Player::reloadAccessDatabase()
{
  Player* playerData;
  for (int i = 0; i < PlayerSlot; i++)
    if ((playerData = playerList[i]) && !playerData->closed)
      playerData->accessInfo.reloadInfo();
}

void GameKeeper::Player::close()
{
  closed = true;
}

bool GameKeeper::Player::clean()
{
  Player* playerData;
  // Trying to detect if this action cleaned the array of player
  bool empty    = true;
  bool ICleaned = false;
  for (int i = 0; i < PlayerSlot; i++)
    if ((playerData = playerList[i])) {
      if (playerData->closed) {
	delete playerData;
	ICleaned = true;
      } else {
	empty = false;
      }
    }
  return empty && ICleaned;
}

int GameKeeper::Player::getFreeIndex(int min, int max)
{
  for (int i = min; i < max; i++)
    if (!playerList[i])
      return i;
  return max;
}

void GameKeeper::Player::handleTcpPacket(fd_set *set)
{
  if (netHandler->isFdSet(set)) {
    const RxStatus e = netHandler->tcpReceive();
    if (e == ReadPart)
      return;
    clientCallback(*netHandler, playerIndex, e);
  }
}

void GameKeeper::Player::setPlayerState(float pos[3], float azimuth)
{
  serverTimeStamp = (float)TimeKeeper::getCurrent().getSeconds();
  memcpy(lastState.pos, pos, sizeof(float) * 3);
  lastState.azimuth = azimuth;
  // Set Speeds to 0 too
  memset(lastState.velocity, 0, sizeof(float) * 3);
  lastState.angVel = 0.0f;
  stateTimeStamp   = 0.0f;

  // player is alive.
  player.setAlive();

}

int GameKeeper::Player::maxShots = 0;
void GameKeeper::Player::setMaxShots(int _maxShots)
{
  maxShots = _maxShots;
}

bool GameKeeper::Player::addShot(int id, int salt, FiringInfo &firingInfo)
{
  float now = (float)TimeKeeper::getCurrent().getSeconds();
  if (id < (int)shotsInfo.size() && shotsInfo[id].present
      && now < shotsInfo[id].expireTime) {
    logDebugMessage(2,"Player %s [%d] shot id %d duplicated\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  // verify shot number
  if (id > maxShots - 1) {
    logDebugMessage(2,"Player %s [%d] shot id %d out of range %d\n",
	  player.getCallSign(), playerIndex, id, maxShots);
    return false;
  }

  shotsInfo.resize(maxShots);

  float lifeTime = BZDB.eval(StateDatabase::BZDB_RELOADTIME);
  if (firingInfo.flagType == Flags::RapidFire)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_RFIREADLIFE);
  else if (firingInfo.flagType == Flags::MachineGun)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_MGUNADLIFE);
  else if (firingInfo.flagType == Flags::GuidedMissile)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_GMADLIFE) + .01f;
  else if (firingInfo.flagType == Flags::Laser)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_LASERADLIFE);
  else if (firingInfo.flagType == Flags::ShockWave)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_SHOCKADLIFE);
  else if (firingInfo.flagType == Flags::Thief)
    lifeTime *= BZDB.eval(StateDatabase::BZDB_THIEFADLIFE);

  ShotInfo myShot;
  myShot.firingInfo  = firingInfo;
  myShot.salt	= salt;
  myShot.expireTime  = now + lifeTime;
  myShot.present     = true;
  myShot.running     = true;

  shotsInfo[id] = myShot;
  return true;
}

bool GameKeeper::Player::removeShot(int id, int salt)
{
  float now = (float)TimeKeeper::getCurrent().getSeconds();
  if (id >= (int)shotsInfo.size() || !shotsInfo[id].present
      || now >= shotsInfo[id].expireTime) {
    logDebugMessage(2,"Player %s [%d] trying to stop the unexistent shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  if (shotsInfo[id].salt != salt) {
    logDebugMessage(2,"Player %s [%d] trying to stop a mismatched shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  if (!shotsInfo[id].running)
    return false;
  shotsInfo[id].running = false;
  return true;
}

bool GameKeeper::Player::updateShot(int id, int salt)
{
  float now = (float)TimeKeeper::getCurrent().getSeconds();
  if (id >= (int)shotsInfo.size() || !shotsInfo[id].present
      || now >= shotsInfo[id].expireTime) {
    logDebugMessage(2,"Player %s [%d] trying to update an unexistent shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  if (shotsInfo[id].salt != salt) {
    logDebugMessage(2,"Player %s [%d] trying to update a mismatched shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  if (!shotsInfo[id].running)
    return false;
  // only GM can be updated
  if (shotsInfo[id].firingInfo.flagType != Flags::GuidedMissile) {
    logDebugMessage(2,"Player %s [%d] trying to update a non GM shot id %d\n",
	  player.getCallSign(), playerIndex, id);
    return false;
  }
  return true;
}

void GameKeeper::Player::setPlayerState(PlayerState state, float timestamp)
{
  lagInfo.updateLag(timestamp, state.order - lastState.order > 1);
  player.updateIdleTime();
  lastState      = state;
  stateTimeStamp = timestamp;
  serverTimeStamp = (float)TimeKeeper::getCurrent().getSeconds();
}

void GameKeeper::Player::getPlayerState(float pos[3], float &azimuth)
{
  memcpy(pos, lastState.pos, sizeof(float) * 3);
  azimuth = lastState.azimuth;
}

void GameKeeper::Player::setLastIdFlag(int _idFlag) {
  idFlag = _idFlag;
}

int GameKeeper::Player::getLastIdFlag() {
  return idFlag;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file 11cxx_async_can.cxx
 * This file implements an asynchronous CAN driver for the LPC11Cxx
 * microcontrollers, using the builtin ROM drivers.
 *
 * @author Balazs Racz
 * @date 14 Dec 2013
 */

#ifdef TARGET_LPC11Cxx

#include "11CXX_rom_driver_CAN.h"
#include "can.h"

#include "executor/control_flow.hxx"
#include "utils/pipe.hxx"

typedef struct _ROM
{
    const unsigned p_usbd;
    const unsigned p_clib;
    const CAND* pCAND;
} ROM;

/** Pointer to the ROM call structures. */
const ROM* const* const rom = (ROM**)0x1fff1ff8;

static const int RX_MSG_OBJ_NUM = 1;
static const int TX_MSG_OBJ_NUM = 2;

extern Executor g_executor;

namespace lpc11cxx
{

#define FIRST_TX_OBJ 1
#define FIRST_RX_OBJ 16

class CanPipeMember;
class CanRxFlow;

/* Static instances of the CAN drivers */
CanPipeMember* g_tx_instance = nullptr;
CanRxFlow* g_rx_instance = nullptr;
Pipe* g_pipe = nullptr;

class CanPipeMember : public PipeMember, public Executable, private Lockable
{
public:
    CanPipeMember()
        : freeTxBuffers_(0xFFFFFFFF), frameToWrite_(nullptr), done_(nullptr), bufferFull_(0)
    {
        lock_.TypedRelease(this);
        released_ = 1;
        allocatorAsked_ = 0;
    }

    virtual AllocatorBase* get_allocator()
    {
        allocatorAsked_++;
        return &lock_;
    }

    virtual void write(const void* buf, size_t count)
    {
        HASSERT(0);
    }

    virtual void async_write(const void* buf, size_t count, Notifiable* done)
    {
        released_ = 0;
        allocatorAsked_--;
        HASSERT(count == sizeof(struct can_frame));
        HASSERT(!frameToWrite_);
        done_ = done;
        LockHolder h(this);
        frameToWrite_ = static_cast<const struct can_frame*>(buf);
        g_executor.Add(this);
    }

    virtual void Run()
    {
        HASSERT(frameToWrite_);
        const struct can_frame* frame = nullptr;
        Notifiable* done = nullptr;
        uint8_t buf_num = FIRST_TX_OBJ;
        {
            LockHolder h(this);

            // Looks for a free tx buffer.
            while (buf_num < FIRST_RX_OBJ &&
                   (!(freeTxBuffers_ & (1 << buf_num))))
            {
                buf_num++;
            }
            if (buf_num >= FIRST_RX_OBJ)
            {
                // Wait for ISR to wake us up.
                bufferFull_ = 1;
                return;
            }

            freeTxBuffers_ &= ~(1 << buf_num);

            // We decided to send the frame.
            frame = frameToWrite_;
            frameToWrite_ = nullptr;  // no callbacks from ISR.
            done = done_;
            done_ = nullptr;
        }
        bufferFull_ = 0;
        CAN_MSG_OBJ msg_obj;
        msg_obj.msgobj = buf_num;
        msg_obj.mode_id = frame->can_id |
                          (frame->can_rtr ? CAN_MSGOBJ_RTR : 0) |
                          (frame->can_eff ? CAN_MSGOBJ_EXT : 0);
        msg_obj.mask = 0x0;
        msg_obj.dlc = frame->can_dlc;
        memcpy(msg_obj.data, frame->data, frame->can_dlc);
        (*rom)->pCAND->can_transmit(&msg_obj);

        // The incoming frame is no longer needed.
        done->Notify();
        released_ = 1;
        lock_.TypedReleaseBack(this);
    }

    void TxFinishedFromIsr(uint8_t buf_num)
    {
        HASSERT(!(freeTxBuffers_ & (1 << buf_num)));
        freeTxBuffers_ |= (1 << buf_num);
        if (frameToWrite_ && !g_executor.IsRunning(this))
        {
            g_executor.AddFromIsr(this);
        }
    }

private:
    uint32_t freeTxBuffers_;
    const struct can_frame* frameToWrite_;
    Notifiable* done_;
    TypedAllocator<PipeMember> lock_;
    // 1 if a run method returned because it didn't find a free tx buffer.
    unsigned bufferFull_ : 1;
    unsigned released_ : 1;
    unsigned allocatorAsked_ : 4;
};

class CanRxFlow : public AllocationResult
{
public:
    CanRxFlow() : bufFull_(0), rxPending_(0), frameLost_(0)
    {
        /* Configures msgobj NN to receive all extended frames. */
        CAN_MSG_OBJ msg_obj;
        msg_obj.msgobj = FIRST_RX_OBJ;
        msg_obj.mode_id = 0x000 | CAN_MSGOBJ_EXT;
        msg_obj.mask = 0x000;
        msg_obj.dlc = 0x000;
        (*rom)->pCAND->config_rxmsgobj(&msg_obj);
    }

    // Callback inside the ISR context. @param buf_num is the number of the
    // message object in the hardware.
    void isr(uint8_t buf_num)
    {
        if (rxPending_)
        {
            frameLost_ = 1;
            // Since rxPending is already 1, we'll let the executor deal with
            // copying the frame to the buffer.
            return;
        }
        else
        {
            rxPending_ = 1;
        }
        if (!bufFull_)
        {
            copy_hardware_frame(buf_num);
            g_executor.AddFromIsr(this);
        }
    }

    // Scheduled by the ISR when the frame has arrived.
    virtual void Run()
    {
        HASSERT(bufFull_);
        g_can_alloc.AllocateEntry(this);
    }

    virtual void AllocationCallback(QueueMember* entry)
    {
        HASSERT(bufFull_);
        CanPipeBuffer* buf = g_can_alloc.cast_result(entry);
        buf->Reset();
        memcpy(&buf->frame, &frame_, sizeof(frame_));
        buf->pipe_buffer.skipMember = g_tx_instance;
        g_pipe->SendBuffer(&buf->pipe_buffer);
        {
            portSET_INTERRUPT_MASK();
            bufFull_ = 0;
            if (rxPending_)
            {
                copy_hardware_frame(FIRST_RX_OBJ);
                // will crash if this is already added
                g_executor.Add(this);
            }
            portCLEAR_INTERRUPT_MASK();
        }
    }

private:
    // Has to run either in ISR or in a critical section. Retrieves a hardware
    // buffer and copies it to the class-local can_frame.
    void copy_hardware_frame(uint8_t buf_num)
    {
        HASSERT(rxPending_);
        HASSERT(!bufFull_);
        CAN_MSG_OBJ msg_obj;
        /* Determine which CAN message has been received */
        msg_obj.msgobj = buf_num;

        /* Now load up the msg_obj structure with the CAN message */
        (*rom)->pCAND->can_receive(&msg_obj);

        // Here we need to be in a critical section; otherwise another CAN
        // interrupt might come in between these two calls.
        bufFull_ = 1;
        rxPending_ = 0;

        frame_.can_id = msg_obj.mode_id & ((1 << 30) - 1);
        HASSERT(frame_.can_id & 0xfff);
        frame_.can_rtr = (msg_obj.mode_id & CAN_MSGOBJ_RTR) ? 1 : 0;
        frame_.can_eff = (msg_obj.mode_id & CAN_MSGOBJ_EXT) ? 1 : 0;
        frame_.can_err = 0;
        frame_.can_dlc = msg_obj.dlc;
        memcpy(frame_.data, msg_obj.data, msg_obj.dlc);
    }

    struct can_frame frame_;
    // 1 if the struct can_frame in here is full.
    unsigned bufFull_ : 1;
    // 1 if the can buffer in the hardware object is full.
    unsigned rxPending_ : 1;
    // 1 if we have lost a frame.
    unsigned frameLost_ : 1;
};

/** CAN receive callback. Called by the ROM can driver in an ISR context.
    @param msg_obj_num the number of CAN buffer that has the new frame.
*/
void CAN_rx(uint8_t msg_obj_num)
{
    HASSERT(g_rx_instance);
    g_rx_instance->isr(msg_obj_num);
    // We always assume there is someone woken up. This is not nice.
    portYIELD();
}

/** CAN transmit callback. Called by the ROM can driver in an ISR context.
    @param msg_obj_num the number of CAN buffer that finished transmission.
*/
void CAN_tx(uint8_t msg_obj_num)
{
    HASSERT(g_tx_instance);
    g_tx_instance->TxFinishedFromIsr(msg_obj_num);
    // We always assume there is someone woken up. This is not nice.
    portYIELD();
}

/** CAN error callback. Called by the ROM can driver in an ISR context.
    @param error_info defines what kind of error occured on the bus.
*/
void CAN_error(uint32_t error_info)
{
    return;
}

/** Function pointer table to pass to the ROM drivers with callbacks. */
static const CAN_CALLBACKS callbacks = {CAN_rx, CAN_tx, CAN_error, NULL,
                                        NULL,   NULL,   NULL,      NULL, };

/**  Clock initialization constants for 125 kbaud */
const uint32_t ClkInitTable125[2] = {0x00000000UL, // CANCLKDIV
                                     0x00001C57UL  // CAN_BTR
};

/**  Clock initialization constants for 250 kbaud */
static const uint32_t ClkInitTable250[2] = {0x00000000UL, // CANCLKDIV
                                            0x00001C4BUL  // CAN_BTR
};

void CreateCanDriver(Pipe* parent)
{
    /* Initialize the CAN controller */
    (*rom)->pCAND->init_can((uint32_t*)&ClkInitTable250[0], 1);
    /* Configure the CAN callback functions */
    (*rom)->pCAND->config_calb((CAN_CALLBACKS*)&callbacks);

    g_pipe = parent;
    g_rx_instance = new CanRxFlow();
    g_tx_instance = new CanPipeMember();
    parent->RegisterMember(g_tx_instance);

    /* Enable the CAN Interrupt */
    NVIC_SetPriority(CAN_IRQn, 1);
    NVIC_EnableIRQ(CAN_IRQn);
}

} // namespace lpc11cxx

extern "C" {
/** Overrides the system's weak interrupt handler and calls the builtin ROM
 * interrupt handler. */
void CAN_IRQHandler(void)
{
    (*rom)->pCAND->isr();
}
}

#else
#error You need to define TARGET_LPC11Cxx if you want to compiple its rom driver.
#endif
<commit_msg>Fixes a bug in the 11CXX asyn can driver. Removes assertion on alias being zero -- JMRI sends frames like that.<commit_after>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file 11cxx_async_can.cxx
 * This file implements an asynchronous CAN driver for the LPC11Cxx
 * microcontrollers, using the builtin ROM drivers.
 *
 * @author Balazs Racz
 * @date 14 Dec 2013
 */

#ifdef TARGET_LPC11Cxx

#include "11CXX_rom_driver_CAN.h"
#include "can.h"

#include "executor/control_flow.hxx"
#include "utils/pipe.hxx"

typedef struct _ROM
{
    const unsigned p_usbd;
    const unsigned p_clib;
    const CAND* pCAND;
} ROM;

/** Pointer to the ROM call structures. */
const ROM* const* const rom = (ROM**)0x1fff1ff8;

static const int RX_MSG_OBJ_NUM = 1;
static const int TX_MSG_OBJ_NUM = 2;

extern Executor g_executor;

namespace lpc11cxx
{

#define FIRST_TX_OBJ 1
#define FIRST_RX_OBJ 16

class CanPipeMember;
class CanRxFlow;

/* Static instances of the CAN drivers */
CanPipeMember* g_tx_instance = nullptr;
CanRxFlow* g_rx_instance = nullptr;
Pipe* g_pipe = nullptr;

class CanPipeMember : public PipeMember, public Executable, private Lockable
{
public:
    CanPipeMember()
        : freeTxBuffers_(0xFFFFFFFF), frameToWrite_(nullptr), done_(nullptr), bufferFull_(0)
    {
        lock_.TypedRelease(this);
        released_ = 1;
        allocatorAsked_ = 0;
    }

    virtual AllocatorBase* get_allocator()
    {
        allocatorAsked_++;
        return &lock_;
    }

    virtual void write(const void* buf, size_t count)
    {
        HASSERT(0);
    }

    virtual void async_write(const void* buf, size_t count, Notifiable* done)
    {
        released_ = 0;
        allocatorAsked_--;
        HASSERT(count == sizeof(struct can_frame));
        HASSERT(!frameToWrite_);
        done_ = done;
        LockHolder h(this);
        frameToWrite_ = static_cast<const struct can_frame*>(buf);
        g_executor.Add(this);
    }

    virtual void Run()
    {
        HASSERT(frameToWrite_);
        const struct can_frame* frame = nullptr;
        Notifiable* done = nullptr;
        uint8_t buf_num = FIRST_TX_OBJ;
        {
            LockHolder h(this);

            // Looks for a free tx buffer.
            while (buf_num < FIRST_RX_OBJ &&
                   (!(freeTxBuffers_ & (1 << buf_num))))
            {
                buf_num++;
            }
            if (buf_num >= FIRST_RX_OBJ)
            {
                // Wait for ISR to wake us up.
                bufferFull_ = 1;
                return;
            }

            freeTxBuffers_ &= ~(1 << buf_num);

            // We decided to send the frame.
            frame = frameToWrite_;
            frameToWrite_ = nullptr;  // no callbacks from ISR.
            done = done_;
            done_ = nullptr;
        }
        bufferFull_ = 0;
        CAN_MSG_OBJ msg_obj;
        msg_obj.msgobj = buf_num;
        msg_obj.mode_id = frame->can_id |
                          (frame->can_rtr ? CAN_MSGOBJ_RTR : 0) |
                          (frame->can_eff ? CAN_MSGOBJ_EXT : 0);
        msg_obj.mask = 0x0;
        msg_obj.dlc = frame->can_dlc;
        memcpy(msg_obj.data, frame->data, frame->can_dlc);
        (*rom)->pCAND->can_transmit(&msg_obj);

        // The incoming frame is no longer needed.
        done->Notify();
        released_ = 1;
        lock_.TypedReleaseBack(this);
    }

    void TxFinishedFromIsr(uint8_t buf_num)
    {
        HASSERT(!(freeTxBuffers_ & (1 << buf_num)));
        freeTxBuffers_ |= (1 << buf_num);
        if (frameToWrite_ && !g_executor.IsRunning(this))
        {
            g_executor.AddFromIsr(this);
        }
    }

private:
    uint32_t freeTxBuffers_;
    const struct can_frame* frameToWrite_;
    Notifiable* done_;
    TypedAllocator<PipeMember> lock_;
    // 1 if a run method returned because it didn't find a free tx buffer.
    unsigned bufferFull_ : 1;
    unsigned released_ : 1;
    unsigned allocatorAsked_ : 4;
};

class CanRxFlow : public AllocationResult
{
public:
    CanRxFlow() : bufFull_(0), rxPending_(0), frameLost_(0)
    {
        /* Configures msgobj NN to receive all extended frames. */
        CAN_MSG_OBJ msg_obj;
        msg_obj.msgobj = FIRST_RX_OBJ;
        msg_obj.mode_id = 0x000 | CAN_MSGOBJ_EXT;
        msg_obj.mask = 0x000;
        msg_obj.dlc = 0x000;
        (*rom)->pCAND->config_rxmsgobj(&msg_obj);
    }

    // Callback inside the ISR context. @param buf_num is the number of the
    // message object in the hardware.
    void isr(uint8_t buf_num)
    {
        if (rxPending_)
        {
            frameLost_ = 1;
            // Since rxPending is already 1, we'll let the executor deal with
            // copying the frame to the buffer.
            return;
        }
        else
        {
            rxPending_ = 1;
        }
        if (!bufFull_)
        {
            copy_hardware_frame(buf_num);
            g_executor.AddFromIsr(this);
        }
    }

    // Scheduled by the ISR when the frame has arrived.
    virtual void Run()
    {
        HASSERT(bufFull_);
        g_can_alloc.AllocateEntry(this);
    }

    virtual void AllocationCallback(QueueMember* entry)
    {
        HASSERT(bufFull_);
        CanPipeBuffer* buf = g_can_alloc.cast_result(entry);
        buf->Reset();
        memcpy(&buf->frame, &frame_, sizeof(frame_));
        buf->pipe_buffer.skipMember = g_tx_instance;
        g_pipe->SendBuffer(&buf->pipe_buffer);
        {
            portSET_INTERRUPT_MASK();
            bufFull_ = 0;
            if (rxPending_)
            {
                copy_hardware_frame(FIRST_RX_OBJ);
                // will crash if this is already added
                g_executor.Add(this);
            }
            portCLEAR_INTERRUPT_MASK();
        }
    }

private:
    // Has to run either in ISR or in a critical section. Retrieves a hardware
    // buffer and copies it to the class-local can_frame.
    void copy_hardware_frame(uint8_t buf_num)
    {
        HASSERT(rxPending_);
        HASSERT(!bufFull_);
        CAN_MSG_OBJ msg_obj;
        /* Determine which CAN message has been received */
        msg_obj.msgobj = buf_num;

        /* Now load up the msg_obj structure with the CAN message */
        (*rom)->pCAND->can_receive(&msg_obj);

        // Here we need to be in a critical section; otherwise another CAN
        // interrupt might come in between these two calls.
        bufFull_ = 1;
        rxPending_ = 0;

        frame_.can_id = msg_obj.mode_id & ((1 << 29) - 1);
        // JMRI crashes the node here.
        // HASSERT(frame_.can_id & 0xfff);
        frame_.can_rtr = (msg_obj.mode_id & CAN_MSGOBJ_RTR) ? 1 : 0;
        frame_.can_eff = (msg_obj.mode_id & CAN_MSGOBJ_EXT) ? 1 : 0;
        frame_.can_err = 0;
        frame_.can_dlc = msg_obj.dlc;
        memcpy(frame_.data, msg_obj.data, msg_obj.dlc);
    }

    struct can_frame frame_;
    // 1 if the struct can_frame in here is full.
    unsigned bufFull_ : 1;
    // 1 if the can buffer in the hardware object is full.
    unsigned rxPending_ : 1;
    // 1 if we have lost a frame.
    unsigned frameLost_ : 1;
};

/** CAN receive callback. Called by the ROM can driver in an ISR context.
    @param msg_obj_num the number of CAN buffer that has the new frame.
*/
void CAN_rx(uint8_t msg_obj_num)
{
    HASSERT(g_rx_instance);
    g_rx_instance->isr(msg_obj_num);
    // We always assume there is someone woken up. This is not nice.
    portYIELD();
}

/** CAN transmit callback. Called by the ROM can driver in an ISR context.
    @param msg_obj_num the number of CAN buffer that finished transmission.
*/
void CAN_tx(uint8_t msg_obj_num)
{
    HASSERT(g_tx_instance);
    g_tx_instance->TxFinishedFromIsr(msg_obj_num);
    // We always assume there is someone woken up. This is not nice.
    portYIELD();
}

/** CAN error callback. Called by the ROM can driver in an ISR context.
    @param error_info defines what kind of error occured on the bus.
*/
void CAN_error(uint32_t error_info)
{
    return;
}

/** Function pointer table to pass to the ROM drivers with callbacks. */
static const CAN_CALLBACKS callbacks = {CAN_rx, CAN_tx, CAN_error, NULL,
                                        NULL,   NULL,   NULL,      NULL, };

/**  Clock initialization constants for 125 kbaud */
const uint32_t ClkInitTable125[2] = {0x00000000UL, // CANCLKDIV
                                     0x00001C57UL  // CAN_BTR
};

/**  Clock initialization constants for 250 kbaud */
static const uint32_t ClkInitTable250[2] = {0x00000000UL, // CANCLKDIV
                                            0x00001C4BUL  // CAN_BTR
};

void CreateCanDriver(Pipe* parent)
{
    /* Initialize the CAN controller */
    (*rom)->pCAND->init_can((uint32_t*)&ClkInitTable250[0], 1);
    /* Configure the CAN callback functions */
    (*rom)->pCAND->config_calb((CAN_CALLBACKS*)&callbacks);

    g_pipe = parent;
    g_rx_instance = new CanRxFlow();
    g_tx_instance = new CanPipeMember();
    parent->RegisterMember(g_tx_instance);

    /* Enable the CAN Interrupt */
    NVIC_SetPriority(CAN_IRQn, 1);
    NVIC_EnableIRQ(CAN_IRQn);
}

} // namespace lpc11cxx

extern "C" {
/** Overrides the system's weak interrupt handler and calls the builtin ROM
 * interrupt handler. */
void CAN_IRQHandler(void)
{
    (*rom)->pCAND->isr();
}
}

#else
#error You need to define TARGET_LPC11Cxx if you want to compiple its rom driver.
#endif
<|endoftext|>


<commit_before>//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id$
//
// Created 2013/06/08
// Author: Mike Ovsiannikov
//
// Copyright 2013 Quantcast Corp.
//
// This file is part of Kosmos File System (KFS).
//
// Licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Kerberos 5 client side authentication implementation.
//
//----------------------------------------------------------------------------

#include "KrbClient.h"

#include <krb5/krb5.h>

#include <string.h>
#include <string>
#include <algorithm>

namespace KFS
{

using std::string;
using std::max;

class KrbClient::Impl
{
public:
    Impl()
        : mServiceHost(),
          mCtx(),
          mAuthCtx(),
          mErrCode(0),
          mOutBuf(),
          mCreds(),
          mServerPtr(0),
          mKeyBlockPtr(0),
          mInitedFlag(false),
          mServiceName(),
          mErrorMsg()
    {
        mOutBuf.data   = 0;
        mOutBuf.length = 0;
    }
    ~Impl()
        {}
    const char* Init(
        const char* inServiceHostNamePtr,
        const char* inServeiceNamePtr)
    {
        CleanupSelf();
        mServiceHost = inServiceHostNamePtr ? inServiceHostNamePtr : "";
        mServiceName = inServeiceNamePtr    ? inServeiceNamePtr    : "";
        mErrCode     = 0;
        mErrorMsg.clear();
        InitSelf();
        if (mErrCode) {
            mErrorMsg = ErrToStr(mErrCode);
            return mErrorMsg.c_str();
        }
        return 0;
    }
    const char* Cleanup()
    {
        mErrorMsg.clear();
        mErrCode = CleanupSelf();
        if (mErrCode) {
            return ErrStr();
        }
        return 0;
    }
    const char* Request(
        const char*& outDataPtr,
        int&         outDataLen)
    {
        if (! mInitedFlag) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not initialized yet, invoke KrbClient::Init";
            return mErrorMsg.c_str();
        }
        CleanupAuth();
        krb5_free_data_contents(mCtx, &mOutBuf);
        krb5_ccache theCache = 0;
	if ((mErrCode = krb5_cc_default(mCtx, &theCache)) != 0) {
            return ErrStr();
        }
        mCreds.server = mServerPtr;
	if ((mErrCode = krb5_cc_get_principal(
                mCtx, theCache, &mCreds.client)) != 0) {
            return ErrStr();
        }
        krb5_creds* theCreds = 0;
	if ((mErrCode = krb5_get_credentials(
                mCtx, 0, theCache, &mCreds, &theCreds)) != 0) {
            return ErrStr();
        }
        krb5_data theAppData = { 0 };
        theAppData.data   = 0;
        theAppData.length = 0;
        if ((mErrCode = krb5_mk_req_extended(
                    mCtx,
                    &mAuthCtx,
                    AP_OPTS_MUTUAL_REQUIRED,
                    &theAppData,
                    theCreds,
                    &mOutBuf)) != 0) {
            return ErrStr();
        }
        outDataPtr = (const char*)mOutBuf.data;
        outDataLen = (int)mOutBuf.length;
        return 0;
    }
    const char* Reply(
        const char*  inReplyPtr,
        int          inReplyLen,
        const char*& outSessionKeyPtr,
        int&         outSessionKeyLen)
    {
        if (! mInitedFlag) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not initialized yet, invoke KrbClient::Init";
            return mErrorMsg.c_str();
        }
        if (mOutBuf.length <= 0 || ! mOutBuf.data) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not ready to process reply, invoke KrbClient::Request";
            return mErrorMsg.c_str();
        }
        if (mKeyBlockPtr) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "possible extraneous invocation of KrbClient::Reply";
            return mErrorMsg.c_str();
        }
        krb5_data theData = { 0 };
        theData.length = max(0, inReplyLen);
        theData.data   = const_cast<char*>(inReplyPtr);
        krb5_ap_rep_enc_part* theReplPtr = 0;
        if ((mErrCode = krb5_rd_rep(
                mCtx,
                mAuthCtx,
                &theData,
                &theReplPtr))) {
            return ErrStr();
        }
        if ((mErrCode = krb5_auth_con_getkey(mCtx, mAuthCtx, &mKeyBlockPtr))) {
            return ErrStr();
        }
        krb5_free_ap_rep_enc_part(mCtx, theReplPtr);
        krb5_free_data_contents(mCtx, &mOutBuf);
        outSessionKeyPtr =
            reinterpret_cast<const char*>(mKeyBlockPtr->contents);
        outSessionKeyLen = (int)mKeyBlockPtr->length;
        return 0;
    }
    int GetErrorCode() const
    {
        return mErrCode;
    }
private:
    string            mServiceHost;
    krb5_context      mCtx;
    krb5_auth_context mAuthCtx;
    krb5_error_code   mErrCode;
    krb5_data         mOutBuf;
    krb5_creds        mCreds;
    krb5_principal    mServerPtr;
    krb5_keyblock*    mKeyBlockPtr;
    bool              mInitedFlag;
    string            mServiceName;
    string            mErrorMsg;

    void InitSelf()
    {
        mErrCode = krb5_init_context(&mCtx);
        if (mErrCode) {
            return;
        }
        mInitedFlag = true;
        memset(&mCreds, 0, sizeof(mCreds));
	if ((mErrCode = krb5_sname_to_principal(
                mCtx,
                mServiceHost.c_str(),
                mServiceName.c_str(),
                KRB5_NT_UNKNOWN, // KRB5_NT_SRV_HST,
                &mServerPtr
            ))) {
            return;
        }
    }
    krb5_error_code CleanupSelf()
    {
        if (! mInitedFlag) {
            return 0;
        }
        krb5_error_code theErr = CleanupAuth();
        mInitedFlag = false;
        memset(&mCreds, 0, sizeof(mCreds));
	if (mServerPtr) {
            krb5_free_principal(mCtx, mServerPtr);
            mServerPtr = 0;
        }
        krb5_free_context(mCtx);
        return theErr;
    }
    krb5_error_code CleanupAuth()
    {
        if (! mInitedFlag) {
            return 0;
        }
	if (mCreds.client) {
            krb5_free_principal(mCtx, mCreds.client);
            mCreds.client = 0;
        }
        if (mKeyBlockPtr) {
            krb5_free_keyblock(mCtx, mKeyBlockPtr);
            mKeyBlockPtr = 0;
        }
        krb5_free_data_contents(mCtx, &mOutBuf);
        if (! mAuthCtx) {
            return 0;
        }
        const krb5_error_code theErr = krb5_auth_con_free(mCtx, mAuthCtx);
        memset(&mCreds, 0, sizeof(mCreds));
        mAuthCtx = 0;
        return theErr;
    }
    const char* ErrStr()
    {
        mErrorMsg = ErrToStr(mErrCode);
        return mErrorMsg.c_str();
    }
    string ErrToStr(
        krb5_error_code inErrCode) const
    {
        if (! inErrCode) {
            return string();
        }
        if ( ! mCtx) {
            return string("no kerberos context");
        }
        const char* const theMsgPtr = krb5_get_error_message(mCtx, inErrCode);
        return string(theMsgPtr);
    }

private:
    Impl(
        const Impl& inImpl);
    Impl& operator=(
        const Impl& inImpl);
};

KrbClient::KrbClient()
    : mImpl(*(new Impl()))
{
}

KrbClient::~KrbClient()
{
    delete &mImpl;
}

    const char*
KrbClient::Init(
    const char* inServiceHostNamePtr,
    const char* inServeiceNamePtr)
{
    return mImpl.Init(inServiceHostNamePtr, inServeiceNamePtr);
}

    const char*
KrbClient::Cleanup()
{
    return mImpl.Cleanup();
}

    const char*
KrbClient::Request(
    const char*& outDataPtr,
    int&         outDataLen)
{
    return mImpl.Request(outDataPtr, outDataLen);
}

    const char*
KrbClient::Reply(
    const char*  inReplyPtr,
    int          inReplyLen,
    const char*& outSessionKeyPtr,
    int&         outSessionKeyLen)
{
    return mImpl.Reply(
        inReplyPtr, inReplyLen, outSessionKeyPtr, outSessionKeyLen);
}

    int
KrbClient::GetErrorCode() const
{
    return mImpl.GetErrorCode();
}

}
<commit_msg>Qfs kerberos lib: fix memory leaks in KrbClient::Request().<commit_after>//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id$
//
// Created 2013/06/08
// Author: Mike Ovsiannikov
//
// Copyright 2013 Quantcast Corp.
//
// This file is part of Kosmos File System (KFS).
//
// Licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Kerberos 5 client side authentication implementation.
//
//----------------------------------------------------------------------------

#include "KrbClient.h"

#include <krb5/krb5.h>

#include <string.h>
#include <string>
#include <algorithm>

namespace KFS
{

using std::string;
using std::max;

class KrbClient::Impl
{
public:
    Impl()
        : mServiceHost(),
          mCtx(),
          mAuthCtx(),
          mErrCode(0),
          mOutBuf(),
          mCreds(),
          mServerPtr(0),
          mKeyBlockPtr(0),
          mInitedFlag(false),
          mServiceName(),
          mErrorMsg()
    {
        mOutBuf.data   = 0;
        mOutBuf.length = 0;
    }
    ~Impl()
        {}
    const char* Init(
        const char* inServiceHostNamePtr,
        const char* inServeiceNamePtr)
    {
        CleanupSelf();
        mServiceHost = inServiceHostNamePtr ? inServiceHostNamePtr : "";
        mServiceName = inServeiceNamePtr    ? inServeiceNamePtr    : "";
        mErrCode     = 0;
        mErrorMsg.clear();
        InitSelf();
        if (mErrCode) {
            mErrorMsg = ErrToStr(mErrCode);
            return mErrorMsg.c_str();
        }
        return 0;
    }
    const char* Cleanup()
    {
        mErrorMsg.clear();
        mErrCode = CleanupSelf();
        if (mErrCode) {
            return ErrStr();
        }
        return 0;
    }
    const char* Request(
        const char*& outDataPtr,
        int&         outDataLen)
    {
        if (! mInitedFlag) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not initialized yet, invoke KrbClient::Init";
            return mErrorMsg.c_str();
        }
        CleanupAuth();
        krb5_free_data_contents(mCtx, &mOutBuf);
        krb5_ccache theCachePtr = 0;
	if ((mErrCode = krb5_cc_default(mCtx, &theCachePtr)) != 0) {
            return ErrStr();
        }
        mCreds.server = mServerPtr;
	if ((mErrCode = krb5_cc_get_principal(
                mCtx, theCachePtr, &mCreds.client)) != 0) {
            krb5_cc_close(mCtx, theCachePtr);
            return ErrStr();
        }
        krb5_creds* theCredsPtr = 0;
	if ((mErrCode = krb5_get_credentials(
                mCtx, 0, theCachePtr, &mCreds, &theCredsPtr)) != 0) {
            krb5_cc_close(mCtx, theCachePtr);
            return ErrStr();
        }
        if ((mErrCode = krb5_cc_close(mCtx, theCachePtr))) {
            krb5_free_creds(mCtx, theCredsPtr);
            return ErrStr();
        }
        krb5_data theAppData = { 0 };
        theAppData.data   = 0;
        theAppData.length = 0;
        mErrCode = krb5_mk_req_extended(
            mCtx,
            &mAuthCtx,
            AP_OPTS_MUTUAL_REQUIRED,
            &theAppData,
            theCredsPtr,
            &mOutBuf
        );
        krb5_free_creds(mCtx, theCredsPtr);
        if (mErrCode != 0) {
            return ErrStr();
        }
        outDataPtr = (const char*)mOutBuf.data;
        outDataLen = (int)mOutBuf.length;
        return 0;
    }
    const char* Reply(
        const char*  inReplyPtr,
        int          inReplyLen,
        const char*& outSessionKeyPtr,
        int&         outSessionKeyLen)
    {
        if (! mInitedFlag) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not initialized yet, invoke KrbClient::Init";
            return mErrorMsg.c_str();
        }
        if (mOutBuf.length <= 0 || ! mOutBuf.data) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not ready to process reply, invoke KrbClient::Request";
            return mErrorMsg.c_str();
        }
        if (mKeyBlockPtr) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "possible extraneous invocation of KrbClient::Reply";
            return mErrorMsg.c_str();
        }
        krb5_data theData = { 0 };
        theData.length = max(0, inReplyLen);
        theData.data   = const_cast<char*>(inReplyPtr);
        krb5_ap_rep_enc_part* theReplPtr = 0;
        if ((mErrCode = krb5_rd_rep(
                mCtx,
                mAuthCtx,
                &theData,
                &theReplPtr))) {
            return ErrStr();
        }
        if ((mErrCode = krb5_auth_con_getkey(mCtx, mAuthCtx, &mKeyBlockPtr))) {
            return ErrStr();
        }
        krb5_free_ap_rep_enc_part(mCtx, theReplPtr);
        krb5_free_data_contents(mCtx, &mOutBuf);
        outSessionKeyPtr =
            reinterpret_cast<const char*>(mKeyBlockPtr->contents);
        outSessionKeyLen = (int)mKeyBlockPtr->length;
        return 0;
    }
    int GetErrorCode() const
    {
        return mErrCode;
    }
private:
    string            mServiceHost;
    krb5_context      mCtx;
    krb5_auth_context mAuthCtx;
    krb5_error_code   mErrCode;
    krb5_data         mOutBuf;
    krb5_creds        mCreds;
    krb5_principal    mServerPtr;
    krb5_keyblock*    mKeyBlockPtr;
    bool              mInitedFlag;
    string            mServiceName;
    string            mErrorMsg;

    void InitSelf()
    {
        mErrCode = krb5_init_context(&mCtx);
        if (mErrCode) {
            return;
        }
        mInitedFlag = true;
        memset(&mCreds, 0, sizeof(mCreds));
	if ((mErrCode = krb5_sname_to_principal(
                mCtx,
                mServiceHost.c_str(),
                mServiceName.c_str(),
                KRB5_NT_UNKNOWN, // KRB5_NT_SRV_HST,
                &mServerPtr
            ))) {
            return;
        }
    }
    krb5_error_code CleanupSelf()
    {
        if (! mInitedFlag) {
            return 0;
        }
        krb5_error_code theErr = CleanupAuth();
        mInitedFlag = false;
        memset(&mCreds, 0, sizeof(mCreds));
	if (mServerPtr) {
            krb5_free_principal(mCtx, mServerPtr);
            mServerPtr = 0;
        }
        krb5_free_context(mCtx);
        return theErr;
    }
    krb5_error_code CleanupAuth()
    {
        if (! mInitedFlag) {
            return 0;
        }
	if (mCreds.client) {
            krb5_free_principal(mCtx, mCreds.client);
            mCreds.client = 0;
        }
        if (mKeyBlockPtr) {
            krb5_free_keyblock(mCtx, mKeyBlockPtr);
            mKeyBlockPtr = 0;
        }
        krb5_free_data_contents(mCtx, &mOutBuf);
        if (! mAuthCtx) {
            return 0;
        }
        const krb5_error_code theErr = krb5_auth_con_free(mCtx, mAuthCtx);
        memset(&mCreds, 0, sizeof(mCreds));
        mAuthCtx = 0;
        return theErr;
    }
    const char* ErrStr()
    {
        mErrorMsg = ErrToStr(mErrCode);
        return mErrorMsg.c_str();
    }
    string ErrToStr(
        krb5_error_code inErrCode) const
    {
        if (! inErrCode) {
            return string();
        }
        if ( ! mCtx) {
            return string("no kerberos context");
        }
        const char* const theMsgPtr = krb5_get_error_message(mCtx, inErrCode);
        return string(theMsgPtr);
    }

private:
    Impl(
        const Impl& inImpl);
    Impl& operator=(
        const Impl& inImpl);
};

KrbClient::KrbClient()
    : mImpl(*(new Impl()))
{
}

KrbClient::~KrbClient()
{
    delete &mImpl;
}

    const char*
KrbClient::Init(
    const char* inServiceHostNamePtr,
    const char* inServeiceNamePtr)
{
    return mImpl.Init(inServiceHostNamePtr, inServeiceNamePtr);
}

    const char*
KrbClient::Cleanup()
{
    return mImpl.Cleanup();
}

    const char*
KrbClient::Request(
    const char*& outDataPtr,
    int&         outDataLen)
{
    return mImpl.Request(outDataPtr, outDataLen);
}

    const char*
KrbClient::Reply(
    const char*  inReplyPtr,
    int          inReplyLen,
    const char*& outSessionKeyPtr,
    int&         outSessionKeyLen)
{
    return mImpl.Reply(
        inReplyPtr, inReplyLen, outSessionKeyPtr, outSessionKeyLen);
}

    int
KrbClient::GetErrorCode() const
{
    return mImpl.GetErrorCode();
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: tokrecv.hxx,v $
 *
 *  $Revision: 1.1.1.1 $
 *
 *  last change: $Author: np $ $Date: 2002-03-08 14:45:35 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef DSAPI_TOKRECV_HXX
#define DSAPI_TOKRECV_HXX

// USED SERVICES
    // BASE CLASSES
    // COMPONENTS
    // PARAMETERS

namespace csi
{
namespace dsapi
{

class Token;
/**
@descr
*/
class Token_Receiver
{
  public:
      virtual               ~Token_Receiver() {}
    virtual void        Receive(
                              DYN Token &           let_drToken ) = 0;
};

}   // namespace dsapi
}   // namespace csi

#endif

<commit_msg>INTEGRATION: CWS adc10 (1.1.1.1.132); FILE MERGED 2005/01/12 13:14:31 np 1.1.1.1.132.1: #i31025# Convenient error log file.<commit_after>/*************************************************************************
 *
 *  $RCSfile: tokrecv.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: obo $ $Date: 2005-01-27 11:30:10 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef DSAPI_TOKRECV_HXX
#define DSAPI_TOKRECV_HXX

// USED SERVICES
    // BASE CLASSES
    // COMPONENTS
    // PARAMETERS

namespace csi
{
namespace dsapi
{

class Token;
/**
@descr
*/
class Token_Receiver
{
  public:
      virtual               ~Token_Receiver() {}
    virtual void        Receive(
                              DYN Token &           let_drToken ) = 0;
    virtual void        Increment_CurLine() = 0;
};

}   // namespace dsapi
}   // namespace csi

#endif

<|endoftext|>


<commit_before>//
//  AmstradCPC.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 30/07/2017.
//  Copyright © 2017 Thomas Harte. All rights reserved.
//

#include "AmstradCPC.hpp"

#include "../../Processors/Z80/Z80.hpp"
#include "../../Components/AY38910/AY38910.hpp"
#include "../../Components/6845/CRTC6845.hpp"

using namespace AmstradCPC;

struct CRTCBusHandler {
	public:
		CRTCBusHandler(uint8_t *ram) :
			cycles_(0),
			was_enabled_(false),
			was_sync_(false),
			pixel_data_(nullptr),
			pixel_pointer_(nullptr),
			ram_(ram) {}

		inline void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
			cycles_++;

			bool is_sync = state.hsync || state.vsync;
			// collect some more pixels if output is ongoing
			if(!is_sync && state.display_enable) {
				if(!pixel_data_) {
					pixel_pointer_ = pixel_data_ = crt_->allocate_write_area(320);
				}
				if(pixel_pointer_) {
					// the CPC shuffles output lines as:
					//	MA12 MA11	RA2 RA1 RA0		MA9 MA8 MA7 MA6 MA5 MA4 MA3 MA2 MA1 MA0		CCLK
					uint16_t address =
						(uint16_t)(
							((state.refresh_address & 0x3FF) << 1) |
							((state.row_address & 7) << 11) |
							((state.refresh_address & 0x1800) << 3)
						);

					*pixel_pointer_++ = ram_[address];

					// flush the current buffer if full
					if(pixel_pointer_ == pixel_data_ + 320) {
						crt_->output_data(320, 16);
						pixel_pointer_ = pixel_data_ = nullptr;
					}
				}
			}

			// if a transition between sync/border/pixels just occurred, announce it
			if(state.display_enable != was_enabled_ || is_sync != was_sync_) {
				if(was_sync_) {
					crt_->output_sync((unsigned int)(cycles_ * 16));
				} else {
					if(was_enabled_) {
						crt_->output_data((unsigned int)(cycles_ * 16), 16);
						pixel_pointer_ = pixel_data_ = nullptr;
					} else {
						uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1);
						if(colour_pointer) *colour_pointer = 0x00;
						crt_->output_level((unsigned int)(cycles_ * 16));
					}
				}

				cycles_ = 0;
				was_sync_ = is_sync;
				was_enabled_ = state.display_enable;
			}
		}

		void setup_output(float aspect_ratio) {
			crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::CRT::DisplayType::PAL50, 1));
			crt_->set_rgb_sampling_function(
				"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
				"{"
					"return vec3(float(texture(texID, coordinate).r) / 255.0);"
				"}");
		}

		void close_output() {
			crt_.reset();
		}

		std::shared_ptr<Outputs::CRT::CRT> get_crt() {
			return crt_;
		}

	private:
		int cycles_;
		bool was_enabled_, was_sync_;
		std::shared_ptr<Outputs::CRT::CRT> crt_;
		uint8_t *pixel_data_, *pixel_pointer_;

		uint8_t *ram_;
};

class ConcreteMachine:
	public CPU::Z80::Processor<ConcreteMachine>,
	public Machine {
	public:
		ConcreteMachine() :
			crtc_counter_(HalfCycles(4)),	// This starts the CRTC exactly out of phase with the memory accesses
			crtc_(crtc_bus_handler_),
			crtc_bus_handler_(ram_) {
			// primary clock is 4Mhz
			set_clock_rate(4000000);
		}

		inline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
			// Amstrad CPC timing scheme: assert WAIT for three out of four cycles
			clock_offset_ = (clock_offset_ + cycle.length) & HalfCycles(7);
			set_wait_line(clock_offset_ >= HalfCycles(2));

			// Update the CRTC once every eight half cycles; aiming for half-cycle 4 as
			// per the initial seed to the crtc_counter_, but any time in the final four
			// will do as it's safe to conclude that nobody else has touched video RAM
			// during that whole window
			crtc_counter_ += cycle.length;
			int crtc_cycles = crtc_counter_.divide(HalfCycles(8)).as_int();
			if(crtc_cycles) crtc_.run_for(Cycles(1));

			// Stop now if no action is strictly required.
			if(!cycle.is_terminal()) return HalfCycles(0);

			uint16_t address = cycle.address ? *cycle.address : 0x0000;
			switch(cycle.operation) {
				case CPU::Z80::PartialMachineCycle::ReadOpcode:
				case CPU::Z80::PartialMachineCycle::Read:
					*cycle.value = read_pointers_[address >> 14][address & 16383];
				break;

				case CPU::Z80::PartialMachineCycle::Write:
					write_pointers_[address >> 14][address & 16383] = *cycle.value;
				break;

				case CPU::Z80::PartialMachineCycle::Output:
					// Check for a gate array access.
					if((address & 0xc000) == 0x4000) {
						switch(*cycle.value >> 6) {
							case 0: printf("Select pen %02x\n", *cycle.value & 0x1f);		break;
							case 1: printf("Select colour %02x\n", *cycle.value & 0x1f);	break;
							case 2:
								printf("Set mode %d, other flags %02x\n", *cycle.value & 3, (*cycle.value >> 2)&7);
								read_pointers_[0] = (*cycle.value & 4) ? &ram_[0] : os_.data();
								read_pointers_[3] = (*cycle.value & 8) ? &ram_[49152] : basic_.data();
							break;
							case 3: printf("RAM paging?\n"); break;
						}
					}

					// Check for a CRTC access
					if(!(address & 0x4000)) {
						switch((address >> 8) & 3) {
							case 0:	crtc_.select_register(*cycle.value);	break;
							case 1:	crtc_.set_register(*cycle.value);		break;
							case 2: case 3:	printf("Illegal CRTC write?\n");	break;
						}
					}

					// Check for a PIO access
					if(!(address & 0x800)) {
						switch((address >> 8) & 3) {
							case 0:	printf("PSG data: %d\n", *cycle.value);	break;
							case 1:	printf("Vsync, etc: %02x\n", *cycle.value);	break;
							case 2:	printf("Key row, etc: %02x\n", *cycle.value);	break;
							case 3:	printf("PIO control: %02x\n", *cycle.value);	break;
						}
					}
				break;
				case CPU::Z80::PartialMachineCycle::Input:
					// Check for a CRTC access
					if(!(address & 0x4000)) {
						switch((address >> 8) & 3) {
							case 0:	case 1: printf("Illegal CRTC read?\n");	break;
							case 2: *cycle.value = crtc_.get_status();		break;
							case 3:	*cycle.value = crtc_.get_register();	break;
						}
					}

					// Check for a PIO access
					if(!(address & 0x800)) {
						switch((address >> 8) & 3) {
							case 0:	printf("[In] PSG data\n");		break;
							case 1:	printf("[In] Vsync, etc\n");	break;
							case 2:	printf("[In] Key row, etc\n");	break;
							case 3:	printf("[In] PIO control\n");	break;
						}
					}

					*cycle.value = 0xff;
				break;

				case CPU::Z80::PartialMachineCycle::Interrupt:
					*cycle.value = 0xff;
				break;

				default: break;
			}

			return HalfCycles(0);
		}

		void flush() {}

		void setup_output(float aspect_ratio) {
			crtc_bus_handler_.setup_output(aspect_ratio);
		}

		void close_output() {
			crtc_bus_handler_.close_output();
		}

		std::shared_ptr<Outputs::CRT::CRT> get_crt() {
			return crtc_bus_handler_.get_crt();
		}

		std::shared_ptr<Outputs::Speaker> get_speaker() {
			return nullptr;
		}

		void run_for(const Cycles cycles) {
			CPU::Z80::Processor<ConcreteMachine>::run_for(cycles);
		}

		void configure_as_target(const StaticAnalyser::Target &target) {
			// Establish reset memory map as per machine model (or, for now, as a hard-wired 464)
			read_pointers_[0] = os_.data();
			read_pointers_[1] = &ram_[16384];
			read_pointers_[2] = &ram_[32768];
			read_pointers_[3] = basic_.data();

			write_pointers_[0] = &ram_[0];
			write_pointers_[1] = &ram_[16384];
			write_pointers_[2] = &ram_[32768];
			write_pointers_[3] = &ram_[49152];
		}

		void set_rom(ROMType type, std::vector<uint8_t> data) {
			// Keep only the two ROMs that are currently of interest.
			switch(type) {
				case ROMType::OS464:		os_ = data;		break;
				case ROMType::BASIC464:		basic_ = data;	break;
				default: break;
			}
		}

	private:
		CRTCBusHandler crtc_bus_handler_;
		Motorola::CRTC::CRTC6845<CRTCBusHandler> crtc_;

		HalfCycles clock_offset_;
		HalfCycles crtc_counter_;

		uint8_t ram_[65536];
		std::vector<uint8_t> os_, basic_;

		uint8_t *read_pointers_[4];
		uint8_t *write_pointers_[4];
};

Machine *Machine::AmstradCPC() {
	return new ConcreteMachine;
}
<commit_msg>Fixed slightly: the CPC wiki has a typo. It's 12 and 13 that move up to 14 and 15.<commit_after>//
//  AmstradCPC.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 30/07/2017.
//  Copyright © 2017 Thomas Harte. All rights reserved.
//

#include "AmstradCPC.hpp"

#include "../../Processors/Z80/Z80.hpp"
#include "../../Components/AY38910/AY38910.hpp"
#include "../../Components/6845/CRTC6845.hpp"

using namespace AmstradCPC;

struct CRTCBusHandler {
	public:
		CRTCBusHandler(uint8_t *ram) :
			cycles_(0),
			was_enabled_(false),
			was_sync_(false),
			pixel_data_(nullptr),
			pixel_pointer_(nullptr),
			ram_(ram) {}

		inline void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
			cycles_++;

			bool is_sync = state.hsync || state.vsync;
			// collect some more pixels if output is ongoing
			if(!is_sync && state.display_enable) {
				if(!pixel_data_) {
					pixel_pointer_ = pixel_data_ = crt_->allocate_write_area(320);
				}
				if(pixel_pointer_) {
					// the CPC shuffles output lines as:
					//	MA13 MA12	RA2 RA1 RA0		MA9 MA8 MA7 MA6 MA5 MA4 MA3 MA2 MA1 MA0		CCLK
					uint16_t address =
						(uint16_t)(
							((state.refresh_address & 0x3ff) << 1) |
							((state.row_address & 0x7) << 11) |
							((state.refresh_address & 0x3000) << 2)
						);

					*pixel_pointer_++ = ram_[address];

					// flush the current buffer if full
					if(pixel_pointer_ == pixel_data_ + 320) {
						crt_->output_data(320, 16);
						pixel_pointer_ = pixel_data_ = nullptr;
					}
				}
			}

			// if a transition between sync/border/pixels just occurred, announce it
			if(state.display_enable != was_enabled_ || is_sync != was_sync_) {
				if(was_sync_) {
					crt_->output_sync((unsigned int)(cycles_ * 16));
				} else {
					if(was_enabled_) {
						crt_->output_data((unsigned int)(cycles_ * 16), 16);
						pixel_pointer_ = pixel_data_ = nullptr;
					} else {
						uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1);
						if(colour_pointer) *colour_pointer = 0x00;
						crt_->output_level((unsigned int)(cycles_ * 16));
					}
				}

				cycles_ = 0;
				was_sync_ = is_sync;
				was_enabled_ = state.display_enable;
			}
		}

		void setup_output(float aspect_ratio) {
			crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::CRT::DisplayType::PAL50, 1));
			crt_->set_rgb_sampling_function(
				"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
				"{"
					"return vec3(float(texture(texID, coordinate).r) / 255.0);"
				"}");
		}

		void close_output() {
			crt_.reset();
		}

		std::shared_ptr<Outputs::CRT::CRT> get_crt() {
			return crt_;
		}

	private:
		int cycles_;
		bool was_enabled_, was_sync_;
		std::shared_ptr<Outputs::CRT::CRT> crt_;
		uint8_t *pixel_data_, *pixel_pointer_;

		uint8_t *ram_;
};

class ConcreteMachine:
	public CPU::Z80::Processor<ConcreteMachine>,
	public Machine {
	public:
		ConcreteMachine() :
			crtc_counter_(HalfCycles(4)),	// This starts the CRTC exactly out of phase with the memory accesses
			crtc_(crtc_bus_handler_),
			crtc_bus_handler_(ram_) {
			// primary clock is 4Mhz
			set_clock_rate(4000000);
		}

		inline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
			// Amstrad CPC timing scheme: assert WAIT for three out of four cycles
			clock_offset_ = (clock_offset_ + cycle.length) & HalfCycles(7);
			set_wait_line(clock_offset_ >= HalfCycles(2));

			// Update the CRTC once every eight half cycles; aiming for half-cycle 4 as
			// per the initial seed to the crtc_counter_, but any time in the final four
			// will do as it's safe to conclude that nobody else has touched video RAM
			// during that whole window
			crtc_counter_ += cycle.length;
			int crtc_cycles = crtc_counter_.divide(HalfCycles(8)).as_int();
			if(crtc_cycles) crtc_.run_for(Cycles(1));

			// Stop now if no action is strictly required.
			if(!cycle.is_terminal()) return HalfCycles(0);

			uint16_t address = cycle.address ? *cycle.address : 0x0000;
			switch(cycle.operation) {
				case CPU::Z80::PartialMachineCycle::ReadOpcode:
				case CPU::Z80::PartialMachineCycle::Read:
					*cycle.value = read_pointers_[address >> 14][address & 16383];
				break;

				case CPU::Z80::PartialMachineCycle::Write:
					write_pointers_[address >> 14][address & 16383] = *cycle.value;
				break;

				case CPU::Z80::PartialMachineCycle::Output:
					// Check for a gate array access.
					if((address & 0xc000) == 0x4000) {
						switch(*cycle.value >> 6) {
							case 0: printf("Select pen %02x\n", *cycle.value & 0x1f);		break;
							case 1: printf("Select colour %02x\n", *cycle.value & 0x1f);	break;
							case 2:
								printf("Set mode %d, other flags %02x\n", *cycle.value & 3, (*cycle.value >> 2)&7);
								read_pointers_[0] = (*cycle.value & 4) ? &ram_[0] : os_.data();
								read_pointers_[3] = (*cycle.value & 8) ? &ram_[49152] : basic_.data();
							break;
							case 3: printf("RAM paging?\n"); break;
						}
					}

					// Check for a CRTC access
					if(!(address & 0x4000)) {
						switch((address >> 8) & 3) {
							case 0:	crtc_.select_register(*cycle.value);	break;
							case 1:	crtc_.set_register(*cycle.value);		break;
							case 2: case 3:	printf("Illegal CRTC write?\n");	break;
						}
					}

					// Check for a PIO access
					if(!(address & 0x800)) {
						switch((address >> 8) & 3) {
							case 0:	printf("PSG data: %d\n", *cycle.value);	break;
							case 1:	printf("Vsync, etc: %02x\n", *cycle.value);	break;
							case 2:	printf("Key row, etc: %02x\n", *cycle.value);	break;
							case 3:	printf("PIO control: %02x\n", *cycle.value);	break;
						}
					}
				break;
				case CPU::Z80::PartialMachineCycle::Input:
					// Check for a CRTC access
					if(!(address & 0x4000)) {
						switch((address >> 8) & 3) {
							case 0:	case 1: printf("Illegal CRTC read?\n");	break;
							case 2: *cycle.value = crtc_.get_status();		break;
							case 3:	*cycle.value = crtc_.get_register();	break;
						}
					}

					// Check for a PIO access
					if(!(address & 0x800)) {
						switch((address >> 8) & 3) {
							case 0:	printf("[In] PSG data\n");		break;
							case 1:	printf("[In] Vsync, etc\n");	break;
							case 2:	printf("[In] Key row, etc\n");	break;
							case 3:	printf("[In] PIO control\n");	break;
						}
					}

					*cycle.value = 0xff;
				break;

				case CPU::Z80::PartialMachineCycle::Interrupt:
					*cycle.value = 0xff;
				break;

				default: break;
			}

			return HalfCycles(0);
		}

		void flush() {}

		void setup_output(float aspect_ratio) {
			crtc_bus_handler_.setup_output(aspect_ratio);
		}

		void close_output() {
			crtc_bus_handler_.close_output();
		}

		std::shared_ptr<Outputs::CRT::CRT> get_crt() {
			return crtc_bus_handler_.get_crt();
		}

		std::shared_ptr<Outputs::Speaker> get_speaker() {
			return nullptr;
		}

		void run_for(const Cycles cycles) {
			CPU::Z80::Processor<ConcreteMachine>::run_for(cycles);
		}

		void configure_as_target(const StaticAnalyser::Target &target) {
			// Establish reset memory map as per machine model (or, for now, as a hard-wired 464)
			read_pointers_[0] = os_.data();
			read_pointers_[1] = &ram_[16384];
			read_pointers_[2] = &ram_[32768];
			read_pointers_[3] = basic_.data();

			write_pointers_[0] = &ram_[0];
			write_pointers_[1] = &ram_[16384];
			write_pointers_[2] = &ram_[32768];
			write_pointers_[3] = &ram_[49152];
		}

		void set_rom(ROMType type, std::vector<uint8_t> data) {
			// Keep only the two ROMs that are currently of interest.
			switch(type) {
				case ROMType::OS464:		os_ = data;		break;
				case ROMType::BASIC464:		basic_ = data;	break;
				default: break;
			}
		}

	private:
		CRTCBusHandler crtc_bus_handler_;
		Motorola::CRTC::CRTC6845<CRTCBusHandler> crtc_;

		HalfCycles clock_offset_;
		HalfCycles crtc_counter_;

		uint8_t ram_[65536];
		std::vector<uint8_t> os_, basic_;

		uint8_t *read_pointers_[4];
		uint8_t *write_pointers_[4];
};

Machine *Machine::AmstradCPC() {
	return new ConcreteMachine;
}
<|endoftext|>