src/fsmblockmap.h
author richk
Tue, 17 Jun 2008 13:41:57 +0000
branchNewGRF_ports
changeset 10995 311b38c7f9a7
parent 6845 666878d09b80
permissions -rw-r--r--
(svn r13549) [NewGRF_ports] -Change: Make recolouring of groundtile (0x0f80) specific to NewGRF_ports only.
Also base groundsprite on airport_tile of station. This prevents mixed colour groundtiles in an airport.
/* $Id$ */

/** @file FSMblockmap.h Definition of FSMblockmap class for use in state machines*/

#ifndef FSMBLOCKMAP_H
#define FSMBLOCKMAP_H

#include "stdafx.h"

uint64 inline GetBlockAsBits(byte blocknumber)
{
	if (blocknumber == 0) return 0ULL;
	if (blocknumber > 63) blocknumber -= 64;
	uint64 result = 1ULL << blocknumber;
	return result;
}

/**
 * This class provides functions and internal storage for blockmaps used in Finite State Machine
 *
*/
struct FSMblockmap {
public:
	void inline ResetAll()
	{
		blocks[0] = 0x0000000000000000ULL;
		blocks[1] = 0x0000000000000000ULL;
	};

	void inline SetAll()
	{
		blocks[0] = 0xFFFFFFFFFFFFFFFFULL;
		blocks[1] = 0xFFFFFFFFFFFFFFFFULL;
	};

	void Initialise(const byte *blocklist, int num_bytes)
	{
		ResetAll();
		for (int i = 0; i < num_bytes; i++) {
			if (blocklist[i] == 0xFF) {
				SetAll();
				break;
			}
			blocks[(blocklist[i] < 64) ? 0 : 1] |= GetBlockAsBits(blocklist[i]);
		}
	}

	uint64 inline GetLower() const
	{
		return blocks[0];
	}

	uint64 inline GetUpper() const
	{
		return blocks[1];
	}

	void inline BlockCopy(const FSMblockmap *blockmap)
	{
		blocks[0] = blockmap->GetLower();
		blocks[1] = blockmap->GetUpper();
	}

	void inline ReserveBlocks(const FSMblockmap *blockmap)
	{
		/* OR with blockmap to reserve blocks */
		blocks[0] |= blockmap->GetLower();
		blocks[1] |= blockmap->GetUpper();
	}

	void inline ReleaseBlocks(const FSMblockmap *blockmap)
	{
		/* AND with ones-complement to clear set bits in blockmap from block_lower and block_upper */
		blocks[0] &= ~blockmap->GetLower();
		blocks[1] &= ~blockmap->GetUpper();
	}

	void inline ReleaseOnlyOwnedBlocks(const FSMblockmap *blockmap, const FSMblockmap *owned_blockmap)
	{
		/* AND with ones-complement to clear set bits in blockmap from block_lower and block_upper */
		blocks[0] &= ~(blockmap->GetLower() & owned_blockmap->GetLower());
		blocks[1] &= ~(blockmap->GetUpper() & owned_blockmap->GetUpper());
	}

	void inline ReleaseLowerBlocks(const FSMblockmap *blockmap)
	{
		/* used to forget any existing terminal reservations */
		blocks[0] &= ~blockmap->GetLower();
	}

	void inline ReleaseUpperBlocks(const FSMblockmap *blockmap)
	{
		/* used to forget any normal movemnt reservations */
		blocks[1] &= ~blockmap->GetUpper();
	}

	bool inline BlocksAreFree(const FSMblockmap *blockmap) const
	{
		uint64 test_lower = blockmap->GetLower();
		uint64 test_upper = blockmap->GetUpper();

		if ((~blocks[0] & test_lower) != test_lower)  return false;
		if ((~blocks[1] & test_upper) != test_upper)  return false;

		return true;
	}

	bool inline CanReserveBlocks(const FSMblockmap *wantedblockmap, const FSMblockmap *targetblockmap) const
	{
		/* HasBlocks is a strict "have I got all the blocks? Yes/No". While useful in places
		 * sometimes you want to ask "ive got some blocks. can I get the rest?"
		 * This function does that.
		 * The first assignment calculates which bits you need, but dont already have.
		 * Then it tests whether the remaining bits are free in targetblockmap.
		 * If they are, then return true.
		 */
		uint64 test_lower = (blocks[0] ^ wantedblockmap->GetLower()) & wantedblockmap->GetLower();
		uint64 test_upper = (blocks[1] ^ wantedblockmap->GetUpper()) & wantedblockmap->GetUpper();

		if ((~targetblockmap->GetLower() & test_lower) != test_lower)  return false;
		if ((~targetblockmap->GetUpper() & test_upper) != test_upper)  return false;

		return true;
	}

	bool inline HasBlocks(const FSMblockmap *blockmap) const
	{
		uint64 test_lower = blockmap->GetLower();
		uint64 test_upper = blockmap->GetUpper();

		if ((blocks[0] & test_lower) != test_lower)  return false;
		if ((blocks[1] & test_upper) != test_upper)  return false;

		return true;
	}

	bool inline HasBlock(byte blocknumber) const
	{
		FSMblockmap test_blockmap;
		test_blockmap.Initialise(&blocknumber, 1);
		return HasBlocks(&test_blockmap);
	}

	//bool inline HasBlock(byte blocknumber)
	//{
	//	uint64 test_block = GetBlockAsBits(blocknumber);
	//	if (blocknumber < 64) return ((block_lower & test_block) == test_block);
	//	return ((block_upper & test_block) == test_block);
	//}

	bool inline IsNotZero(void) const
	{
		return ((blocks[0] != 0) || (blocks[1] != 0));
	}

	void PrintBlock(bool newline = true) const;

	/**
	 * Needed for an ugly hack:
	 *  - vehicles and stations need to store fsmblocks, so they use FSMBlock as container
	 *  - this internals of the container should be protected, e.g. private (or protected) by C++
	 *  - for saving/loading we need to pass pointer to objects
	 *  -> so *if* the pointer to the FSMBlock is the same as the pointer to the blocks array
	 *     encapsulated in the FSMBlock, we can just pass the FSMBlock as "offset".
	 *     Normally we would then just add the offset of the blocks variable within the class
	 *     but that is not possible because the variable is private. Furthermore we are not sure
	 *     that this works on all platforms, we need to check whether the offset is actually 0.
	 *     This cannot be done compile time, because the variable is private. So we need to write
	 *     a function that does actually check the offset runtime and call it somewhere where it
	 *     is always called but it should not be called often.
	 */
	static void AssertOnWrongBlockOffset();

private:
	uint64 blocks[2];
};

FSMblockmap* SetFSMSingleBlock(const byte *blocklist);

#endif /* FSMBLOCKMAP_H */