<commit_before>#include "srs_figure_rtmp_chunk.h"
#include "../app/srs_figure_app_log.h"

using namespace srs_rtmp_chunk;
rtmp_chunk::rtmp_chunk(unsigned long lChunkStreamID,unsigned char cMessageType,long lTimeStamp):
		mChunkBasicHeader(basic_header_error),
		mpBasicHeader(nullptr),
		mTimeStamp(lTimeStamp),
		mMessageStreamID(0),
		mMessageStreamType(cMessageType),
		mChunkStreamID(lChunkStreamID),
		max_chunk_data_size(128)
{
	// format mpBasicHeader
	if(mChunkStreamID < 64)
	{
		mpBasicHeader = new char[1];
		mpBasicHeader[0] = mChunkStreamID;
	}
	else if(mChunkStreamID < 319)
	{
		mpBasicHeader = new char[2];
		mChunkBasicHeader = basic_header_type1;

		mpBasicHeader[0] = 0;
		mpBasicHeader[1] = mChunkStreamID - 64;
	}
	else if(mChunkStreamID <= 65599)
	{
		mpBasicHeader = new char[3];
		mChunkBasicHeader = basic_header_type2;

		mpBasicHeader[0] = 1;
		if(mChunkStreamID % 256 < 64)
		{
			mpBasicHeader[2] = mChunkStreamID / 256 - 1;
			mpBasicHeader[1] = mChunkStreamID - mpBasicHeader[2] * 256 - 64;
		}
		else
		{
			mpBasicHeader[2] = mChunkStreamID / 256;
			mpBasicHeader[1] = mChunkStreamID - mpBasicHeader[2] * 256 - 64;
		}
	}
	else
	{
		mChunkBasicHeader = basic_header_error;
		srs_figure_log::getInstance()->log("Error","rtmp chunk create","fail to initalize chunk header,the chunk stream ID %d is out of range",mChunkStreamID);
	}
}

rtmp_chunk::~rtmp_chunk()
{
	if(mpBasicHeader != nullptr) delete[] mpBasicHeader;
}

std::vector<std::string> rtmp_chunk::AssembleOneDataChunk(std::string pMsg,enChunkDataType chunkType,enMessageCtrlTypeID msgCtrlTypeID, long MsgStreamID,long timeStamp)
{
	mChunkList.clear();
	if(mChunkBasicHeader ==basic_header_error) return mChunkList;

	if(pMsg.size() > max_chunk_data_size)
	{
		int loopTimes = pMsg.size() / max_chunk_data_size;
		// the first package, we should set the message header as type 0
		std::string chunkPackage_0,chunkHeader_0;
		AssembleDataHeader(chunkHeader_0,THIRD_CHUNK,chunkType,msgCtrlTypeID,MsgStreamID,timeStamp);
		if(chunkHeader_0.size() > 0)
		{
			chunkPackage_0 += chunkHeader_0;
			chunkPackage_0 += pMsg.substr(0,max_chunk_data_size-1);
			mChunkList.push_back(chunkPackage_0);
		}
		// middle package the message header is type 3
		int i = 1;
		for(i = 1; i < loopTimes; i++)// const size aka max_chunk_data_size
		{
			std::string chunkPackage,chunkHeader;
			AssembleDataHeader(chunkHeader,THIRD_CHUNK,chunkType,msgCtrlTypeID,MsgStreamID,timeStamp);
			if(chunkHeader.size() > 0)
			{
				chunkPackage += chunkHeader;
				chunkPackage += pMsg.substr(i * max_chunk_data_size,(i+1) * max_chunk_data_size-1);
				mChunkList.push_back(chunkPackage);
			}
		}
		// the last package, the chunkdata size is not const and message header is type 3
		std::string chunkPackage,chunkHeader;
		AssembleDataHeader(chunkHeader,THIRD_CHUNK,chunkType,msgCtrlTypeID,MsgStreamID,timeStamp);
		if(chunkHeader.size() > 0)
		{
			chunkPackage += chunkHeader;
			chunkPackage += pMsg.substr(i * max_chunk_data_size-1,pMsg.size());
			mChunkList.push_back(chunkPackage);
		}
	}
	else
	{
		// message header is type 0
		std::string chunkPackage,chunkHeader;
		AssembleDataHeader(chunkHeader,ZEARO_CHUNK,chunkType,msgCtrlTypeID,MsgStreamID,timeStamp);
		if(chunkHeader.size() > 0)
		{
			chunkPackage += chunkHeader;
			chunkPackage += pMsg;
			mChunkList.push_back(chunkPackage);
		}
	}

	return mChunkList;
}

long rtmp_chunk::AssembleDataHeader(std::string& msg ,chunk_state chunkState,enChunkDataType ChunkType,enMessageCtrlTypeID msgCtrlTypeID, long MsgStreamID,long timeStamp)
{
	char* pMessageHeader = nullptr;
	int msgSize = 0,timeForWrite = 0;
	char* p = nullptr;
	switch(chunkState)
	{
	case ZEARO_CHUNK:
		// FMT is 0
		mpBasicHeader[0] = mpBasicHeader[0] & 0x3F;
		pMessageHeader = new char[11];
		// write timestamp 3 bytes
		timeForWrite = timeStamp < 0xFFFFFF ? timeStamp:0xFFFFFF;
		p = (char*)&timeForWrite;
		pMessageHeader[0] = p[0];
		pMessageHeader[1] = p[1];
		pMessageHeader[2] = p[2];

		// write message length 3 bytes
		p = (char*)&msgSize;
		pMessageHeader[3] = p[0];
		pMessageHeader[4] = p[1];
		pMessageHeader[5] = p[2];

		//message type id, 1 bytes
		pMessageHeader[6] = ChunkType == VIDEO_DATA_CHUNK ? 9 :(ChunkType == AUDIO_DATA_CHUNK ? 8 : msgCtrlTypeID);

		//message stream ID;
		p = (char*)MsgStreamID;
		pMessageHeader[7] = p[0];
		pMessageHeader[8] = p[1];
		pMessageHeader[9] = p[2];
		pMessageHeader[10] = p[3];
		break;

	case FIRST_CHUNK:
		// FMT is 1
		mpBasicHeader[0] = (mpBasicHeader[0] & 0x3F) | 0x80;
		pMessageHeader = new char[3];
		break;

	case SECOND_CHUNK:
		// FMT is 2
		mpBasicHeader[0] = (mpBasicHeader[0] & 0x3F) | 0xC0;
		break;

	case THIRD_CHUNK:
		// FMT is 3
		mpBasicHeader[0] = (mpBasicHeader[0] & 0x3F) | 0xF0;
		break;
	}
	//make up the chunk header
	msg += mpBasicHeader;
	msg += pMessageHeader;

	// judge for extern timestamp
	//

	if(pMessageHeader != nullptr) delete[] pMessageHeader;
	return RESULT_OK;
}

std::vector<std::string> rtmp_chunk::AssembleOneControlChunk(enMessageCtrlTypeID msgCtrlTypeID, long MsgStreamID,long acknowledgementSize ,char limitType)
{
	mChunkList.clear();
	// In this situation we send only on chunk package
	std::string strPayload;
	char* tmp = nullptr;
	switch(msgCtrlTypeID)
	{
	case MESSAGE_CONTROL_TYPE_1:
		// get chunk size
		tmp = (char*)&max_chunk_data_size;
		tmp[0] &=0x7F;
		strPayload = tmp;
		break;
	case MESSAGE_CONTROL_TYPE_2:
		// abort message
		tmp = (char*)&mChunkStreamID;
		strPayload = tmp;
		break;
	case MESSAGE_CONTROL_TYPE_3:
	case MESSAGE_CONTROL_TYPE_5:
		// acknowledgement
		tmp = (char*)&acknowledgementSize;
		strPayload = tmp;
		break;
	case MESSAGE_CONTROL_TYPE_6:
		tmp = (char*)&acknowledgementSize;
		strPayload = tmp;
		strPayload += limitType;
		break;
	default:
		return mChunkList;
	}
	return AssembleOneDataChunk(strPayload,MESSAGE_CONTROL_CHUNK,msgCtrlTypeID,MsgStreamID);
}

void rtmp_chunk::DecodeOndeDataChunk(std::string pMsg)
{
	int chunk_stream_id = 0;
	const char* cpMsg = pMsg.c_str();
	// decode basic header
	int bhType = cpMsg[0] & 0x3F;// judge the basic header type
	if(bhType == 0)// 2 bytes
	{
		chunk_stream_id = 256 * pMsg[2] + pMsg[1] + 64;
	}
	else if(bhType == 1)// 3 bytes
	{
		chunk_stream_id = pMsg[1] + 64;
	}
	else// 1 bytes
	{
		chunk_stream_id = pMsg[0] & 0x3F;
	}

	switch(pMsg[0] & 0xC0)
	{
	case 0:
		break;
	case 1:
		break;
	case 2:
		break;
	case 3:
		break;
	default:
		srs_figure_log::getInstance()->log("Error","decodeChunk","get error chunk package");
		return;
	}

	// decode chunk header
	// get payload
}
<commit_msg>finish decoding one chunk<commit_after>#include "srs_figure_rtmp_chunk.h"
#include "../app/srs_figure_app_log.h"

using namespace srs_rtmp_chunk;
rtmp_chunk::rtmp_chunk(unsigned long lChunkStreamID,unsigned char cMessageType,long lTimeStamp):
		mChunkBasicHeader(basic_header_error),
		mpBasicHeader(nullptr),
		mTimeStamp(lTimeStamp),
		mMessageStreamID(0),
		mMessageStreamType(cMessageType),
		mChunkStreamID(lChunkStreamID),
		max_chunk_data_size(128)
{
	// format mpBasicHeader
	if(mChunkStreamID < 64)
	{
		mpBasicHeader = new char[1];
		mpBasicHeader[0] = mChunkStreamID;
	}
	else if(mChunkStreamID < 319)
	{
		mpBasicHeader = new char[2];
		mChunkBasicHeader = basic_header_type1;

		mpBasicHeader[0] = 0;
		mpBasicHeader[1] = mChunkStreamID - 64;
	}
	else if(mChunkStreamID <= 65599)
	{
		mpBasicHeader = new char[3];
		mChunkBasicHeader = basic_header_type2;

		mpBasicHeader[0] = 1;
		if(mChunkStreamID % 256 < 64)
		{
			mpBasicHeader[2] = mChunkStreamID / 256 - 1;
			mpBasicHeader[1] = mChunkStreamID - mpBasicHeader[2] * 256 - 64;
		}
		else
		{
			mpBasicHeader[2] = mChunkStreamID / 256;
			mpBasicHeader[1] = mChunkStreamID - mpBasicHeader[2] * 256 - 64;
		}
	}
	else
	{
		mChunkBasicHeader = basic_header_error;
		srs_figure_log::getInstance()->log("Error","rtmp chunk create","fail to initalize chunk header,the chunk stream ID %d is out of range",mChunkStreamID);
	}
}

rtmp_chunk::~rtmp_chunk()
{
	if(mpBasicHeader != nullptr) delete[] mpBasicHeader;
}

std::vector<std::string> rtmp_chunk::AssembleOneDataChunk(std::string pMsg,enChunkDataType chunkType,enMessageCtrlTypeID msgCtrlTypeID, long MsgStreamID,long timeStamp)
{
	mChunkList.clear();
	if(mChunkBasicHeader ==basic_header_error) return mChunkList;

	if(pMsg.size() > max_chunk_data_size)
	{
		int loopTimes = pMsg.size() / max_chunk_data_size;
		// the first package, we should set the message header as type 0
		std::string chunkPackage_0,chunkHeader_0;
		AssembleDataHeader(chunkHeader_0,THIRD_CHUNK,chunkType,msgCtrlTypeID,MsgStreamID,timeStamp);
		if(chunkHeader_0.size() > 0)
		{
			chunkPackage_0 += chunkHeader_0;
			chunkPackage_0 += pMsg.substr(0,max_chunk_data_size-1);
			mChunkList.push_back(chunkPackage_0);
		}
		// middle package the message header is type 3
		int i = 1;
		for(i = 1; i < loopTimes; i++)// const size aka max_chunk_data_size
		{
			std::string chunkPackage,chunkHeader;
			AssembleDataHeader(chunkHeader,THIRD_CHUNK,chunkType,msgCtrlTypeID,MsgStreamID,timeStamp);
			if(chunkHeader.size() > 0)
			{
				chunkPackage += chunkHeader;
				chunkPackage += pMsg.substr(i * max_chunk_data_size,(i+1) * max_chunk_data_size-1);
				mChunkList.push_back(chunkPackage);
			}
		}
		// the last package, the chunkdata size is not const and message header is type 3
		std::string chunkPackage,chunkHeader;
		AssembleDataHeader(chunkHeader,THIRD_CHUNK,chunkType,msgCtrlTypeID,MsgStreamID,timeStamp);
		if(chunkHeader.size() > 0)
		{
			chunkPackage += chunkHeader;
			chunkPackage += pMsg.substr(i * max_chunk_data_size-1,pMsg.size());
			mChunkList.push_back(chunkPackage);
		}
	}
	else
	{
		// message header is type 0
		std::string chunkPackage,chunkHeader;
		AssembleDataHeader(chunkHeader,ZEARO_CHUNK,chunkType,msgCtrlTypeID,MsgStreamID,timeStamp);
		if(chunkHeader.size() > 0)
		{
			chunkPackage += chunkHeader;
			chunkPackage += pMsg;
			mChunkList.push_back(chunkPackage);
		}
	}

	return mChunkList;
}

long rtmp_chunk::AssembleDataHeader(std::string& msg ,chunk_state chunkState,enChunkDataType ChunkType,enMessageCtrlTypeID msgCtrlTypeID, long MsgStreamID,long timeStamp)
{
	char* pMessageHeader = nullptr;
	int msgSize = 0,timeForWrite = 0;
	char* p = nullptr;
	switch(chunkState)
	{
	case ZEARO_CHUNK:
		// FMT is 0
		mpBasicHeader[0] = mpBasicHeader[0] & 0x3F;
		pMessageHeader = new char[11];
		// write timestamp 3 bytes
		timeForWrite = timeStamp < 0xFFFFFF ? timeStamp:0xFFFFFF;
		p = (char*)&timeForWrite;
		pMessageHeader[0] = p[0];
		pMessageHeader[1] = p[1];
		pMessageHeader[2] = p[2];

		// write message length 3 bytes
		p = (char*)&msgSize;
		pMessageHeader[3] = p[0];
		pMessageHeader[4] = p[1];
		pMessageHeader[5] = p[2];

		//message type id, 1 bytes
		pMessageHeader[6] = ChunkType == VIDEO_DATA_CHUNK ? 9 :(ChunkType == AUDIO_DATA_CHUNK ? 8 : msgCtrlTypeID);

		//message stream ID;
		p = (char*)MsgStreamID;
		pMessageHeader[7] = p[0];
		pMessageHeader[8] = p[1];
		pMessageHeader[9] = p[2];
		pMessageHeader[10] = p[3];
		break;

	case FIRST_CHUNK:
		// FMT is 1
		mpBasicHeader[0] = (mpBasicHeader[0] & 0x3F) | 0x80;
		pMessageHeader = new char[3];
		break;

	case SECOND_CHUNK:
		// FMT is 2
		mpBasicHeader[0] = (mpBasicHeader[0] & 0x3F) | 0xC0;
		break;

	case THIRD_CHUNK:
		// FMT is 3
		mpBasicHeader[0] = (mpBasicHeader[0] & 0x3F) | 0xF0;
		break;
	}
	//make up the chunk header
	msg += mpBasicHeader;
	msg += pMessageHeader;

	// judge for extern timestamp
	//

	if(pMessageHeader != nullptr) delete[] pMessageHeader;
	return RESULT_OK;
}

std::vector<std::string> rtmp_chunk::AssembleOneControlChunk(enMessageCtrlTypeID msgCtrlTypeID, long MsgStreamID,long acknowledgementSize ,char limitType)
{
	mChunkList.clear();
	// In this situation we send only on chunk package
	std::string strPayload;
	char* tmp = nullptr;
	switch(msgCtrlTypeID)
	{
	case MESSAGE_CONTROL_TYPE_1:
		// get chunk size
		tmp = (char*)&max_chunk_data_size;
		tmp[0] &=0x7F;
		strPayload = tmp;
		break;
	case MESSAGE_CONTROL_TYPE_2:
		// abort message
		tmp = (char*)&mChunkStreamID;
		strPayload = tmp;
		break;
	case MESSAGE_CONTROL_TYPE_3:
	case MESSAGE_CONTROL_TYPE_5:
		// acknowledgement
		tmp = (char*)&acknowledgementSize;
		strPayload = tmp;
		break;
	case MESSAGE_CONTROL_TYPE_6:
		tmp = (char*)&acknowledgementSize;
		strPayload = tmp;
		strPayload += limitType;
		break;
	default:
		return mChunkList;
	}
	return AssembleOneDataChunk(strPayload,MESSAGE_CONTROL_CHUNK,msgCtrlTypeID,MsgStreamID);
}

void rtmp_chunk::DecodeOndeDataChunk(std::string pMsg)
{
	int chunk_stream_id = 0,offset = 0;
	const char* cpMsg = pMsg.c_str();

	// extra info
	int32_t timestamp = 0;
	int32_t messageLength = 0;
	char	messageTypeId = 0;
	int32_t messageStreamID = 0;
	int32_t messageHeaderType = 0;
	std::string payload = "";
	// decode basic header
	int bhType = cpMsg[0] & 0x3F;// judge the basic header type
	if(bhType == 0)// 2 bytes
	{
		chunk_stream_id = 256 * pMsg[2] + pMsg[1] + 64;
		offset += 2;
	}
	else if(bhType == 1)// 3 bytes
	{
		chunk_stream_id = pMsg[1] + 64;
		offset += 3;
	}
	else// 1 bytes
	{
		chunk_stream_id = pMsg[0] & 0x3F;
		offset += 1;
	}

	char* pMsgHeader = nullptr;
	char* tmp = nullptr;
	messageHeaderType = pMsg[0] & 0xC0;
	switch(messageHeaderType)
	{
	case 0:
		pMsgHeader = new char[11];
		memcpy(pMsgHeader,cpMsg+offset,11);
		// time stamp
		tmp = (char*)&timestamp;
		tmp[2] = pMsgHeader[0];
		tmp[1] = pMsgHeader[1];
		tmp[0] = pMsgHeader[2];

		// message stream lenght
		tmp = (char*)&messageLength;
		tmp[2] = pMsgHeader[3];
		tmp[1] = pMsgHeader[4];
		tmp[0] = pMsgHeader[5];

		// message type id
		messageTypeId = pMsgHeader[6];

		// message stream id
		tmp = (char*)&messageStreamID;
		tmp[3] = pMsgHeader[7];
		tmp[2] = pMsgHeader[8];
		tmp[1] = pMsgHeader[9];
		tmp[0] = pMsgHeader[10];

		offset += 11;
		break;
	case 1:
		pMsgHeader = new char[7];
		memcpy(pMsgHeader,cpMsg+offset,7);
		offset += 7;
		// time stamp
		tmp = (char*)&timestamp;
		tmp[2] = pMsgHeader[0];
		tmp[1] = pMsgHeader[1];
		tmp[0] = pMsgHeader[2];

		// message stream lenght
		tmp = (char*)&messageLength;
		tmp[2] = pMsgHeader[3];
		tmp[1] = pMsgHeader[4];
		tmp[0] = pMsgHeader[5];

		// message type id
		messageTypeId = pMsgHeader[6];
		break;
	case 2:
		pMsgHeader = new char[3];
		memcpy(pMsgHeader,cpMsg+offset,3);
		offset += 3;
		// time stamp
		tmp = (char*)&timestamp;
		tmp[2] = pMsgHeader[0];
		tmp[1] = pMsgHeader[1];
		tmp[0] = pMsgHeader[2];
		break;
	case 3:
		break;
	default:
		srs_figure_log::getInstance()->log("Error","decodeChunk","get error chunk package");
		return;
	}
	payload += &pMsgHeader[offset];

	if(chunk_stream_id == 2)
	{
		// control message
		const char* pPayload = payload.c_str();
		int32_t getChunkStreamID = 0;
		int32_t getSequenceNum = 0;
		int32_t getAcknowledgeSize = 0;
		char getLimitType = 0;
		switch (messageTypeId)
		{
		case 1:
			tmp = (char*)&max_chunk_data_size;
			tmp [3] = pPayload[0];
			tmp [2] = pPayload[1];
			tmp [1] = pPayload[2];
			tmp [0] = pPayload[3];
			break;
		case 2:
			tmp = (char*)&getChunkStreamID;
			tmp [3] = pPayload[0];
			tmp [2] = pPayload[1];
			tmp [1] = pPayload[2];
			tmp [0] = pPayload[3];
			break;
		case 3:
			tmp = (char*)&getSequenceNum;
			tmp [3] = pPayload[0];
			tmp [2] = pPayload[1];
			tmp [1] = pPayload[2];
			tmp [0] = pPayload[3];
			break;
		case 5:
			tmp = (char*)&getAcknowledgeSize;
			tmp [3] = pPayload[0];
			tmp [2] = pPayload[1];
			tmp [1] = pPayload[2];
			tmp [0] = pPayload[3];
			break;
		case 6:
			tmp = (char*)&getAcknowledgeSize;
			tmp [3] = pPayload[0];
			tmp [2] = pPayload[1];
			tmp [1] = pPayload[2];
			tmp [0] = pPayload[3];

			getLimitType = pPayload[4];
			break;
		}
	}
	// get payload
	if(pMsgHeader != nullptr) delete[] pMsgHeader;
}
<|endoftext|>


<commit_before>//
//  File: RenderCommandEncoder.cpp
//  Author: Hongtae Kim (tiff2766@gmail.com)
//
//  Copyright (c) 2016-2017 Hongtae Kim. All rights reserved.
//

#include "../GraphicsAPI.h"
#if DKGL_ENABLE_VULKAN

#include "RenderCommandEncoder.h"
#include "Texture.h"
#include "GraphicsDevice.h"

using namespace DKFramework;
using namespace DKFramework::Private::Vulkan;

RenderCommandEncoder::Resources::Resources(CommandBuffer* b)
	: cb(b)
	, framebuffer(VK_NULL_HANDLE)
	, renderPass(VK_NULL_HANDLE)
	, commandBuffer(VK_NULL_HANDLE)
{
}

RenderCommandEncoder::Resources::~Resources(void)
{
	GraphicsDevice* dev = (GraphicsDevice*)DKGraphicsDeviceInterface::Instance(cb->Queue()->Device());
	VkDevice device = dev->device;

	if (renderPass)
		vkDestroyRenderPass(device, renderPass, nullptr);

	if (framebuffer)
		vkDestroyFramebuffer(device, framebuffer, nullptr);

	if (commandBuffer)
		cb->ReleaseEncodingBuffer(commandBuffer);
}

RenderCommandEncoder::RenderCommandEncoder(VkCommandBuffer vcb, CommandBuffer* cb, const DKRenderPassDescriptor& desc)
	: commandBuffer(cb)
{
	resources = DKOBJECT_NEW Resources(cb);
	resources->commandBuffer = vcb;
	DKASSERT_DEBUG(resources->commandBuffer);

	uint32_t frameWidth = 0;
	uint32_t frameHeight = 0;

	DKArray<VkAttachmentDescription> attachments;
	attachments.Reserve(desc.colorAttachments.Count() + 1);

	DKArray<VkAttachmentReference> colorReferences;
	colorReferences.Reserve(desc.colorAttachments.Count());

	DKArray<VkImageView> framebufferImageViews;
	framebufferImageViews.Reserve(desc.colorAttachments.Count() + 1);

	DKArray<VkClearValue> attachmentClearValues;
	attachmentClearValues.Reserve(desc.colorAttachments.Count() + 1);

	for (const DKRenderPassColorAttachmentDescriptor& colorAttachment : desc.colorAttachments)
	{
		const Texture* rt = colorAttachment.renderTarget.SafeCast<Texture>();
		if (rt)
		{
			AddWaitSemaphore(rt->waitSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
			AddSignalSemaphore(rt->signalSemaphore);

			VkAttachmentDescription attachment = {};
			attachment.format = rt->format;
			attachment.samples = VK_SAMPLE_COUNT_1_BIT;
			switch (colorAttachment.loadAction)
			{
			case DKRenderPassAttachmentDescriptor::LoadActionLoad:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
				break;
			case DKRenderPassAttachmentDescriptor::LoadActionClear:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
				break;
			default:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
				break;
			}
			switch (colorAttachment.storeAction)
			{
			case DKRenderPassAttachmentDescriptor::StoreActionDontCare:
				attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
				break;
			case DKRenderPassAttachmentDescriptor::StoreActionStore:
				attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
				break;
			}
			attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
			attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
			attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

			VkAttachmentReference attachmentReference = {};
			attachmentReference.attachment = static_cast<uint32_t>(attachments.Count());
			attachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

			attachments.Add(attachment);
			colorReferences.Add(attachmentReference);

			framebufferImageViews.Add(rt->imageView);

			VkClearValue clearValue = {};
			clearValue.color = { colorAttachment.clearColor.r, colorAttachment.clearColor.g, colorAttachment.clearColor.b, colorAttachment.clearColor.a };
			attachmentClearValues.Add(clearValue);

			frameWidth = (frameWidth > 0) ? Min(frameWidth, rt->Width()) : rt->Width();
			frameHeight = (frameHeight > 0) ? Min(frameHeight, rt->Height()) : rt->Height();
		}
	}

	VkAttachmentReference depthStencilReference = {};
	depthStencilReference.attachment = VK_ATTACHMENT_UNUSED;
	depthStencilReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

	if (desc.depthStencilAttachment.renderTarget)
	{
		const DKRenderPassDepthStencilAttachmentDescriptor& depthStencilAttachment = desc.depthStencilAttachment;
		const Texture* rt = depthStencilAttachment.renderTarget.SafeCast<Texture>();
		if (rt)
		{
			AddWaitSemaphore(rt->waitSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
			AddSignalSemaphore(rt->signalSemaphore);

			VkAttachmentDescription attachment = {};
			attachment.format = rt->format;
			attachment.samples = VK_SAMPLE_COUNT_1_BIT;
			switch (depthStencilAttachment.loadAction)
			{
			case DKRenderPassAttachmentDescriptor::LoadActionLoad:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
				break;
			case DKRenderPassAttachmentDescriptor::LoadActionClear:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
				break;
			default:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
				break;
			}
			switch (depthStencilAttachment.storeAction)
			{
			case DKRenderPassAttachmentDescriptor::StoreActionDontCare:
				attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
				break;
			case DKRenderPassAttachmentDescriptor::StoreActionStore:
				attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
				break;
			}
			attachment.stencilLoadOp = attachment.loadOp;
			attachment.stencilStoreOp = attachment.storeOp;
			attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

			depthStencilReference.attachment = static_cast<uint32_t>(attachments.Count());
			attachments.Add(attachment);

			framebufferImageViews.Add(rt->imageView);

			VkClearValue clearValue = {};
			clearValue.depthStencil.depth = depthStencilAttachment.clearDepth;
			clearValue.depthStencil.stencil = depthStencilAttachment.clearStencil;
			attachmentClearValues.Add(clearValue);

			frameWidth = (frameWidth > 0) ? Min(frameWidth, rt->Width()) : rt->Width();
			frameHeight = (frameHeight > 0) ? Min(frameHeight, rt->Height()) : rt->Height();
		}
	}

	VkSubpassDescription subpassDescription = {};
	subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
	subpassDescription.colorAttachmentCount = static_cast<uint32_t>(colorReferences.Count());
	subpassDescription.pColorAttachments = colorReferences;
	subpassDescription.pDepthStencilAttachment = &depthStencilReference;
	subpassDescription.inputAttachmentCount = 0;
	subpassDescription.pInputAttachments = nullptr;
	subpassDescription.preserveAttachmentCount = 0;
	subpassDescription.pPreserveAttachments = nullptr;
	subpassDescription.pResolveAttachments = nullptr;

	VkRenderPassCreateInfo  renderPassCreateInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
	renderPassCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.Count());
	renderPassCreateInfo.pAttachments = attachments;
	renderPassCreateInfo.subpassCount = 1;
	renderPassCreateInfo.pSubpasses = &subpassDescription;

	GraphicsDevice* dev = (GraphicsDevice*)DKGraphicsDeviceInterface::Instance(commandBuffer->Queue()->Device());
	VkDevice device = dev->device;

	VkResult err = vkCreateRenderPass(device, &renderPassCreateInfo, nullptr, &resources->renderPass);
	if (err != VK_SUCCESS)
	{
		DKLogE("ERROR: vkCreateRenderPass failed: %s", VkResultCStr(err));
		DKASSERT(err == VK_SUCCESS);
	}

	VkFramebufferCreateInfo frameBufferCreateInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
	frameBufferCreateInfo.renderPass = resources->renderPass;
	frameBufferCreateInfo.attachmentCount = static_cast<uint32_t>(framebufferImageViews.Count());
	frameBufferCreateInfo.pAttachments = framebufferImageViews;
	frameBufferCreateInfo.width = frameWidth;
	frameBufferCreateInfo.height = frameHeight;
	frameBufferCreateInfo.layers = 1;
	err = vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &resources->framebuffer);
	if (err != VK_SUCCESS)
	{
		DKLogE("ERROR: vkCreateFramebuffer failed: %s", VkResultCStr(err));
		DKASSERT(err == VK_SUCCESS);
	}

	VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
	vkBeginCommandBuffer(resources->commandBuffer, &commandBufferBeginInfo);

	VkRenderPassBeginInfo renderPassBeginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
	renderPassBeginInfo.renderPass = resources->renderPass;
	renderPassBeginInfo.clearValueCount = static_cast<uint32_t>(attachmentClearValues.Count());
	renderPassBeginInfo.pClearValues = attachmentClearValues;
	renderPassBeginInfo.renderArea.offset.x = 0;
	renderPassBeginInfo.renderArea.offset.y = 0;
	renderPassBeginInfo.renderArea.extent.width = frameWidth;
	renderPassBeginInfo.renderArea.extent.height = frameHeight;
	renderPassBeginInfo.framebuffer = resources->framebuffer;
	vkCmdBeginRenderPass(resources->commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);

	VkViewport viewport = {};
	viewport.height = (float)frameHeight;
	viewport.width = (float)frameWidth;
	viewport.minDepth = (float) 0.0f;
	viewport.maxDepth = (float) 1.0f;
	vkCmdSetViewport(resources->commandBuffer, 0, 1, &viewport);
}

RenderCommandEncoder::~RenderCommandEncoder(void)
{
	resources = NULL;
}

void RenderCommandEncoder::AddWaitSemaphore(VkSemaphore semaphore, VkPipelineStageFlags flags)
{
	if (semaphore != VK_NULL_HANDLE)
	{
		if (!semaphorePipelineStageMasks.Insert(semaphore, flags))
			semaphorePipelineStageMasks.Value(semaphore) |= flags;
	}
}

void RenderCommandEncoder::AddSignalSemaphore(VkSemaphore semaphore)
{
	if (semaphore != VK_NULL_HANDLE)
		signalSemaphores.Insert(semaphore);
}

void RenderCommandEncoder::EndEncoding(void)
{
	vkCmdEndRenderPass(resources->commandBuffer);
	VkResult err = vkEndCommandBuffer(resources->commandBuffer);

	if (err != VK_SUCCESS)
	{
		DKLogE("ERROR: vkEndCommandBuffer failed: %s", VkResultCStr(err));
		DKASSERT(err == VK_SUCCESS);
	}

	VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
	submitInfo.commandBufferCount = 1;
	submitInfo.pCommandBuffers = &resources->commandBuffer;

	resources->waitSemaphores.Reserve(semaphorePipelineStageMasks.Count());
	resources->waitStageMasks.Reserve(semaphorePipelineStageMasks.Count());

	semaphorePipelineStageMasks.EnumerateForward([&](decltype(semaphorePipelineStageMasks)::Pair& pair)
	{
		resources->waitSemaphores.Add(pair.key);
		resources->waitStageMasks.Add(pair.value);
	});

	resources->signalSemaphores.Reserve(signalSemaphores.Count());
	signalSemaphores.EnumerateForward([&](VkSemaphore semaphore)
	{
		resources->signalSemaphores.Add(semaphore);
	});

	DKASSERT_DEBUG(resources->waitSemaphores.Count() == resources->waitStageMasks.Count());

	submitInfo.waitSemaphoreCount = resources->waitSemaphores.Count();
	submitInfo.pWaitSemaphores = resources->waitSemaphores;
	submitInfo.pWaitDstStageMask = resources->waitStageMasks;
	submitInfo.signalSemaphoreCount = resources->signalSemaphores.Count();
	submitInfo.pSignalSemaphores = resources->signalSemaphores;

	commandBuffer->Submit(submitInfo, DKFunction([=](DKObject<Resources> res)
	{
		res = NULL;
	})->Invocation(resources));

	resources = NULL;
	semaphorePipelineStageMasks.Clear();
	signalSemaphores.Clear();
}

DKCommandBuffer* RenderCommandEncoder::Buffer(void)
{
	return commandBuffer;
}

#endif //#if DKGL_ENABLE_VULKAN
<commit_msg>no message<commit_after>//
//  File: RenderCommandEncoder.cpp
//  Author: Hongtae Kim (tiff2766@gmail.com)
//
//  Copyright (c) 2016-2017 Hongtae Kim. All rights reserved.
//

#include "../GraphicsAPI.h"
#if DKGL_ENABLE_VULKAN

#include "RenderCommandEncoder.h"
#include "Texture.h"
#include "GraphicsDevice.h"

using namespace DKFramework;
using namespace DKFramework::Private::Vulkan;

RenderCommandEncoder::Resources::Resources(CommandBuffer* b)
	: cb(b)
	, framebuffer(VK_NULL_HANDLE)
	, renderPass(VK_NULL_HANDLE)
	, commandBuffer(VK_NULL_HANDLE)
{
}

RenderCommandEncoder::Resources::~Resources(void)
{
	GraphicsDevice* dev = (GraphicsDevice*)DKGraphicsDeviceInterface::Instance(cb->Queue()->Device());
	VkDevice device = dev->device;

	if (renderPass)
		vkDestroyRenderPass(device, renderPass, nullptr);

	if (framebuffer)
		vkDestroyFramebuffer(device, framebuffer, nullptr);

	if (commandBuffer)
		cb->ReleaseEncodingBuffer(commandBuffer);
}

RenderCommandEncoder::RenderCommandEncoder(VkCommandBuffer vcb, CommandBuffer* cb, const DKRenderPassDescriptor& desc)
	: commandBuffer(cb)
{
	resources = DKOBJECT_NEW Resources(cb);
	resources->commandBuffer = vcb;
	DKASSERT_DEBUG(resources->commandBuffer);

	uint32_t frameWidth = 0;
	uint32_t frameHeight = 0;

	DKArray<VkAttachmentDescription> attachments;
	attachments.Reserve(desc.colorAttachments.Count() + 1);

	DKArray<VkAttachmentReference> colorReferences;
	colorReferences.Reserve(desc.colorAttachments.Count());

	DKArray<VkImageView> framebufferImageViews;
	framebufferImageViews.Reserve(desc.colorAttachments.Count() + 1);

	DKArray<VkClearValue> attachmentClearValues;
	attachmentClearValues.Reserve(desc.colorAttachments.Count() + 1);

	for (const DKRenderPassColorAttachmentDescriptor& colorAttachment : desc.colorAttachments)
	{
		const Texture* rt = colorAttachment.renderTarget.SafeCast<Texture>();
		if (rt)
		{
			AddWaitSemaphore(rt->waitSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
			AddSignalSemaphore(rt->signalSemaphore);

			VkAttachmentDescription attachment = {};
			attachment.format = rt->format;
			attachment.samples = VK_SAMPLE_COUNT_1_BIT; // 1 sample per pixel
			switch (colorAttachment.loadAction)
			{
			case DKRenderPassAttachmentDescriptor::LoadActionLoad:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
				break;
			case DKRenderPassAttachmentDescriptor::LoadActionClear:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
				break;
			default:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
				break;
			}
			switch (colorAttachment.storeAction)
			{
			case DKRenderPassAttachmentDescriptor::StoreActionDontCare:
				attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
				break;
			case DKRenderPassAttachmentDescriptor::StoreActionStore:
				attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
				break;
			}
			attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
			attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
			attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

			VkAttachmentReference attachmentReference = {};
			attachmentReference.attachment = static_cast<uint32_t>(attachments.Count());
			attachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

			attachments.Add(attachment);
			colorReferences.Add(attachmentReference);

			framebufferImageViews.Add(rt->imageView);

			VkClearValue clearValue = {};
			clearValue.color = { colorAttachment.clearColor.r, colorAttachment.clearColor.g, colorAttachment.clearColor.b, colorAttachment.clearColor.a };
			attachmentClearValues.Add(clearValue);

			frameWidth = (frameWidth > 0) ? Min(frameWidth, rt->Width()) : rt->Width();
			frameHeight = (frameHeight > 0) ? Min(frameHeight, rt->Height()) : rt->Height();
		}
	}

	VkAttachmentReference depthStencilReference = {};
	depthStencilReference.attachment = VK_ATTACHMENT_UNUSED;
	depthStencilReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

	if (desc.depthStencilAttachment.renderTarget)
	{
		const DKRenderPassDepthStencilAttachmentDescriptor& depthStencilAttachment = desc.depthStencilAttachment;
		const Texture* rt = depthStencilAttachment.renderTarget.SafeCast<Texture>();
		if (rt)
		{
			AddWaitSemaphore(rt->waitSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
			AddSignalSemaphore(rt->signalSemaphore);

			VkAttachmentDescription attachment = {};
			attachment.format = rt->format;
			attachment.samples = VK_SAMPLE_COUNT_1_BIT;
			switch (depthStencilAttachment.loadAction)
			{
			case DKRenderPassAttachmentDescriptor::LoadActionLoad:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
				break;
			case DKRenderPassAttachmentDescriptor::LoadActionClear:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
				break;
			default:
				attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
				break;
			}
			switch (depthStencilAttachment.storeAction)
			{
			case DKRenderPassAttachmentDescriptor::StoreActionDontCare:
				attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
				break;
			case DKRenderPassAttachmentDescriptor::StoreActionStore:
				attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
				break;
			}
			attachment.stencilLoadOp = attachment.loadOp;
			attachment.stencilStoreOp = attachment.storeOp;
			attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

			depthStencilReference.attachment = static_cast<uint32_t>(attachments.Count());
			attachments.Add(attachment);

			framebufferImageViews.Add(rt->imageView);

			VkClearValue clearValue = {};
			clearValue.depthStencil.depth = depthStencilAttachment.clearDepth;
			clearValue.depthStencil.stencil = depthStencilAttachment.clearStencil;
			attachmentClearValues.Add(clearValue);

			frameWidth = (frameWidth > 0) ? Min(frameWidth, rt->Width()) : rt->Width();
			frameHeight = (frameHeight > 0) ? Min(frameHeight, rt->Height()) : rt->Height();
		}
	}

	VkSubpassDescription subpassDescription = {};
	subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
	subpassDescription.colorAttachmentCount = static_cast<uint32_t>(colorReferences.Count());
	subpassDescription.pColorAttachments = colorReferences;
	subpassDescription.pDepthStencilAttachment = &depthStencilReference;
	subpassDescription.inputAttachmentCount = 0;
	subpassDescription.pInputAttachments = nullptr;
	subpassDescription.preserveAttachmentCount = 0;
	subpassDescription.pPreserveAttachments = nullptr;
	subpassDescription.pResolveAttachments = nullptr;

	VkRenderPassCreateInfo  renderPassCreateInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
	renderPassCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.Count());
	renderPassCreateInfo.pAttachments = attachments;
	renderPassCreateInfo.subpassCount = 1;
	renderPassCreateInfo.pSubpasses = &subpassDescription;

	GraphicsDevice* dev = (GraphicsDevice*)DKGraphicsDeviceInterface::Instance(commandBuffer->Queue()->Device());
	VkDevice device = dev->device;

	VkResult err = vkCreateRenderPass(device, &renderPassCreateInfo, nullptr, &resources->renderPass);
	if (err != VK_SUCCESS)
	{
		DKLogE("ERROR: vkCreateRenderPass failed: %s", VkResultCStr(err));
		DKASSERT(err == VK_SUCCESS);
	}

	VkFramebufferCreateInfo frameBufferCreateInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
	frameBufferCreateInfo.renderPass = resources->renderPass;
	frameBufferCreateInfo.attachmentCount = static_cast<uint32_t>(framebufferImageViews.Count());
	frameBufferCreateInfo.pAttachments = framebufferImageViews;
	frameBufferCreateInfo.width = frameWidth;
	frameBufferCreateInfo.height = frameHeight;
	frameBufferCreateInfo.layers = 1;
	err = vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &resources->framebuffer);
	if (err != VK_SUCCESS)
	{
		DKLogE("ERROR: vkCreateFramebuffer failed: %s", VkResultCStr(err));
		DKASSERT(err == VK_SUCCESS);
	}

	VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
	vkBeginCommandBuffer(resources->commandBuffer, &commandBufferBeginInfo);

	VkRenderPassBeginInfo renderPassBeginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
	renderPassBeginInfo.renderPass = resources->renderPass;
	renderPassBeginInfo.clearValueCount = static_cast<uint32_t>(attachmentClearValues.Count());
	renderPassBeginInfo.pClearValues = attachmentClearValues;
	renderPassBeginInfo.renderArea.offset.x = 0;
	renderPassBeginInfo.renderArea.offset.y = 0;
	renderPassBeginInfo.renderArea.extent.width = frameWidth;
	renderPassBeginInfo.renderArea.extent.height = frameHeight;
	renderPassBeginInfo.framebuffer = resources->framebuffer;
	vkCmdBeginRenderPass(resources->commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);

	VkViewport viewport = {};
	viewport.height = (float)frameHeight;
	viewport.width = (float)frameWidth;
	viewport.minDepth = (float) 0.0f;
	viewport.maxDepth = (float) 1.0f;
	vkCmdSetViewport(resources->commandBuffer, 0, 1, &viewport);
}

RenderCommandEncoder::~RenderCommandEncoder(void)
{
	resources = NULL;
}

void RenderCommandEncoder::AddWaitSemaphore(VkSemaphore semaphore, VkPipelineStageFlags flags)
{
	if (semaphore != VK_NULL_HANDLE)
	{
		if (!semaphorePipelineStageMasks.Insert(semaphore, flags))
			semaphorePipelineStageMasks.Value(semaphore) |= flags;
	}
}

void RenderCommandEncoder::AddSignalSemaphore(VkSemaphore semaphore)
{
	if (semaphore != VK_NULL_HANDLE)
		signalSemaphores.Insert(semaphore);
}

void RenderCommandEncoder::EndEncoding(void)
{
	vkCmdEndRenderPass(resources->commandBuffer);
	VkResult err = vkEndCommandBuffer(resources->commandBuffer);

	if (err != VK_SUCCESS)
	{
		DKLogE("ERROR: vkEndCommandBuffer failed: %s", VkResultCStr(err));
		DKASSERT(err == VK_SUCCESS);
	}

	VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
	submitInfo.commandBufferCount = 1;
	submitInfo.pCommandBuffers = &resources->commandBuffer;

	resources->waitSemaphores.Reserve(semaphorePipelineStageMasks.Count());
	resources->waitStageMasks.Reserve(semaphorePipelineStageMasks.Count());

	semaphorePipelineStageMasks.EnumerateForward([&](decltype(semaphorePipelineStageMasks)::Pair& pair)
	{
		resources->waitSemaphores.Add(pair.key);
		resources->waitStageMasks.Add(pair.value);
	});

	resources->signalSemaphores.Reserve(signalSemaphores.Count());
	signalSemaphores.EnumerateForward([&](VkSemaphore semaphore)
	{
		resources->signalSemaphores.Add(semaphore);
	});

	DKASSERT_DEBUG(resources->waitSemaphores.Count() == resources->waitStageMasks.Count());

	submitInfo.waitSemaphoreCount = resources->waitSemaphores.Count();
	submitInfo.pWaitSemaphores = resources->waitSemaphores;
	submitInfo.pWaitDstStageMask = resources->waitStageMasks;
	submitInfo.signalSemaphoreCount = resources->signalSemaphores.Count();
	submitInfo.pSignalSemaphores = resources->signalSemaphores;

	commandBuffer->Submit(submitInfo, DKFunction([=](DKObject<Resources> res)
	{
		res = NULL;
	})->Invocation(resources));

	resources = NULL;
	semaphorePipelineStageMasks.Clear();
	signalSemaphores.Clear();
}

DKCommandBuffer* RenderCommandEncoder::Buffer(void)
{
	return commandBuffer;
}

#endif //#if DKGL_ENABLE_VULKAN
<|endoftext|>


<commit_before>/** @file
 *
 * @ingroup dspSoundFileLib
 *
 * @brief Tests for the #TTSoundfile class
 *
 * @details Tests the core functions of the TTSoundfile class in order to ensure that things are working after a build. It also demostrate how to make calls to common methods within the class.@n
 * IMPORTANT NOTE: Because POSIX filepaths will be specific to your system, it is important to change the TESTFILE definitions in this file to match a local absolute address. The related TEST definitions should also be set to match the attribution of the file which can be obtained via your sound file editor of choice.
 *
 * @authors Nathan Wolek
 *
 * @copyright Copyright © 2013 by Nathan Wolek @n
 * This code is licensed under the terms of the "New BSD License" @n
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTSoundfile.h"
#include "TTUnitTest.h"

/*
 #define TESTFILE "/Users/nathanwolek/Desktop/geese_clip.aif"
 #define TESTNUMCHANNELS 2
 #define TESTSAMPLERATE 44100
 #define TESTDURATIONINSAMPLES 88202
 #define TESTDURATIONINSECONDS 2.00004535
 #define TESTTITLE ""
 #define TESTARTIST ""
 #define TESTDATE ""
 #define TESTANNOTATION ""
 */

/* */
 #define TESTFILE "/Volumes/Storage/Audio/200604femf15/pitched/ding_b2.aiff"
#define TESTNUMCHANNELS 1
#define TESTSAMPLERATE 44100
#define TESTDURATIONINSAMPLES 39493
#define TESTDURATIONINSECONDS 0.89553288
#define TESTTITLE ""
#define TESTARTIST ""
#define TESTDATE ""
#define TESTANNOTATION ""
/* */

/*
 #define TESTFILE "/Volumes/Storage/Audio/200604femf15/ambience/street.aiff"
 #define TESTNUMCHANNELS 1
 #define TESTSAMPLERATE 44100
 #define TESTDURATIONINSAMPLES 4750848
 #define TESTDURATIONINSECONDS 107.728980
 #define TESTTITLE "UF Street"
 #define TESTARTIST "MPG"
 #define TESTDATE "2006"
 #define TESTANNOTATION ""
 */

TTErr TTSoundfile::test(TTValue& returnedTestInfo)
{
    int errorCount = 0;
    int testAssertionCount = 0;

    {

		TTTestLog("\n");
		TTTestLog("Testing TTSoundfile Basics...");

		TTSoundfilePtr soundfile = NULL;
		TTErr err;

        // TEST 0: instantiate the object to a pointer
        TTBoolean result0 = { TTObjectBaseInstantiate("soundfile", (TTObjectBasePtr*)&soundfile, kTTValNONE) == kTTErrNone};

		TTTestAssertion("instantiates successfully",
						result0,
						testAssertionCount,
						errorCount);

        // TEST 1: set the filepath
        TTBoolean result1 = { soundfile->setFilePath(TT(TESTFILE)) == kTTErrNone };

        TTTestAssertion("setFilePath operates successfully",
                        result1,
                        testAssertionCount,
                        errorCount);

        // TEST 2: reports correct number of channels
        TTColumnID return2 = soundfile->getNumChannels();
        TTBoolean result2 = { return2 == TESTNUMCHANNELS };

        TTTestAssertion("reports the correct number of channels",
                        result2,
                        testAssertionCount,
                        errorCount);

        if(!result2)
        {
            TTTestLog("Expected a value of %i, but returned value was %i", TESTNUMCHANNELS, return2);
        }

        // TEST 3: reports correct sample rate
        TTFloat64 return3 = soundfile->getSampleRate();
        TTBoolean result3 = TTTestFloatEquivalence(return3, TESTSAMPLERATE, true, 0.0000001);

        TTTestAssertion("reports the correct sample rate",
                        result3,
                        testAssertionCount,
                        errorCount);

        if(!result3)
        {
            TTTestLog("Expected a value of %f, but returned value was %f", TESTSAMPLERATE, return3);
        }

        // TEST 4: reports correct duration in samples
        TTRowID return4 = soundfile->getLengthInSamples();
        TTBoolean result4 = { return4 == TESTDURATIONINSAMPLES };

        TTTestAssertion("reports the correct duration in samples",
                        result4,
                        testAssertionCount,
                        errorCount);

        if(!result4)
        {
            TTTestLog("Expected a value of %i, but returned value was %i", TESTDURATIONINSAMPLES, return4);
        }

        // TEST 5: reports correct duration in seconds
        TTFloat64 return5 = soundfile->getLengthInSeconds();
        TTBoolean result5 = TTTestFloatEquivalence(return5, TESTDURATIONINSECONDS, true, 0.0000001);

        TTTestAssertion("reports the correct duration in seconds",
                        result5,
                        testAssertionCount,
                        errorCount);

        if(!result5)
        {
            TTTestLog("Expected a value of %f, but returned value was %f", TESTDURATIONINSECONDS, return5);
        }

        TTTestLog("\n");
		TTTestLog("Testing TTSoundfile Metadata...");

        // TEST 6: reports correct title from metadata
        TTSymbol return6 = soundfile->getTitle();

        TTTestLog("Expected metadata title:");
        TTTestLog(TESTTITLE);
        TTTestLog("Returned metadata title:");
        TTTestLog(return6.c_str());

        // TEST 7: reports correct artist from metadata
        TTSymbol return7 = soundfile->getArtist();

        TTTestLog("Expected metadata artist:");
        TTTestLog(TESTARTIST);
        TTTestLog("Returned metadata artist:");
        TTTestLog(return7.c_str());

        // TEST 8: reports correct title from metadata
        TTSymbol return8 = soundfile->getDate();

        TTTestLog("Expected metadata date:");
        TTTestLog(TESTDATE);
        TTTestLog("Returned metadata date:");
        TTTestLog(return8.c_str());

        // TEST 9: reports correct artist from metadata
        TTSymbol return9 = soundfile->getAnnotation();

        TTTestLog("Expected metadata comment:");
        TTTestLog(TESTANNOTATION);
        TTTestLog("Returned metadata comment:");
        TTTestLog(return9.c_str());

        TTTestLog("\n");
		TTTestLog("Testing peek method on first 10 sample values...");

        TTSampleValue return10;
        TTErr error10;

        for (int channel=0;channel<return2;channel++)
        {
            TTTestLog("Channel %i", channel);
            for (int sample=0;sample<10;sample++)
            {
                error10 = soundfile->peek(sample,channel,return10);
                if (error10 == kTTErrNone)
                {
                    TTTestLog("peek sample %i returned the value %f", sample, return10);
                } else {
                    TTTestLog("peek returned an error for sample %i", sample);
                }
            }
        }

    }

    return TTTestFinish(testAssertionCount, errorCount, returnedTestInfo);
}
<commit_msg>created very basic test of the peeki() method<commit_after>/** @file
 *
 * @ingroup dspSoundFileLib
 *
 * @brief Tests for the #TTSoundfile class
 *
 * @details Tests the core functions of the TTSoundfile class in order to ensure that things are working after a build. It also demostrate how to make calls to common methods within the class.@n
 * IMPORTANT NOTE: Because POSIX filepaths will be specific to your system, it is important to change the TESTFILE definitions in this file to match a local absolute address. The related TEST definitions should also be set to match the attribution of the file which can be obtained via your sound file editor of choice.
 *
 * @authors Nathan Wolek
 *
 * @copyright Copyright © 2013 by Nathan Wolek @n
 * This code is licensed under the terms of the "New BSD License" @n
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTSoundfile.h"
#include "TTUnitTest.h"

/*
 #define TESTFILE "/Users/nathanwolek/Desktop/geese_clip.aif"
 #define TESTNUMCHANNELS 2
 #define TESTSAMPLERATE 44100
 #define TESTDURATIONINSAMPLES 88202
 #define TESTDURATIONINSECONDS 2.00004535
 #define TESTTITLE ""
 #define TESTARTIST ""
 #define TESTDATE ""
 #define TESTANNOTATION ""
 */

/* */
 #define TESTFILE "/Volumes/Storage/Audio/200604femf15/pitched/ding_b2.aiff"
#define TESTNUMCHANNELS 1
#define TESTSAMPLERATE 44100
#define TESTDURATIONINSAMPLES 39493
#define TESTDURATIONINSECONDS 0.89553288
#define TESTTITLE ""
#define TESTARTIST ""
#define TESTDATE ""
#define TESTANNOTATION ""
/* */

/*
 #define TESTFILE "/Volumes/Storage/Audio/200604femf15/ambience/street.aiff"
 #define TESTNUMCHANNELS 1
 #define TESTSAMPLERATE 44100
 #define TESTDURATIONINSAMPLES 4750848
 #define TESTDURATIONINSECONDS 107.728980
 #define TESTTITLE "UF Street"
 #define TESTARTIST "MPG"
 #define TESTDATE "2006"
 #define TESTANNOTATION ""
 */

TTErr TTSoundfile::test(TTValue& returnedTestInfo)
{
    int errorCount = 0;
    int testAssertionCount = 0;

    {

		TTTestLog("\n");
		TTTestLog("Testing TTSoundfile Basics...");

		TTSoundfilePtr soundfile = NULL;
		TTErr err;

        // TEST 0: instantiate the object to a pointer
        TTBoolean result0 = { TTObjectBaseInstantiate("soundfile", (TTObjectBasePtr*)&soundfile, kTTValNONE) == kTTErrNone};

		TTTestAssertion("instantiates successfully",
						result0,
						testAssertionCount,
						errorCount);

        // TEST 1: set the filepath
        TTBoolean result1 = { soundfile->setFilePath(TT(TESTFILE)) == kTTErrNone };

        TTTestAssertion("setFilePath operates successfully",
                        result1,
                        testAssertionCount,
                        errorCount);

        // TEST 2: reports correct number of channels
        TTColumnID return2 = soundfile->getNumChannels();
        TTBoolean result2 = { return2 == TESTNUMCHANNELS };

        TTTestAssertion("reports the correct number of channels",
                        result2,
                        testAssertionCount,
                        errorCount);

        if(!result2)
        {
            TTTestLog("Expected a value of %i, but returned value was %i", TESTNUMCHANNELS, return2);
        }

        // TEST 3: reports correct sample rate
        TTFloat64 return3 = soundfile->getSampleRate();
        TTBoolean result3 = TTTestFloatEquivalence(return3, TESTSAMPLERATE, true, 0.0000001);

        TTTestAssertion("reports the correct sample rate",
                        result3,
                        testAssertionCount,
                        errorCount);

        if(!result3)
        {
            TTTestLog("Expected a value of %f, but returned value was %f", TESTSAMPLERATE, return3);
        }

        // TEST 4: reports correct duration in samples
        TTRowID return4 = soundfile->getLengthInSamples();
        TTBoolean result4 = { return4 == TESTDURATIONINSAMPLES };

        TTTestAssertion("reports the correct duration in samples",
                        result4,
                        testAssertionCount,
                        errorCount);

        if(!result4)
        {
            TTTestLog("Expected a value of %i, but returned value was %i", TESTDURATIONINSAMPLES, return4);
        }

        // TEST 5: reports correct duration in seconds
        TTFloat64 return5 = soundfile->getLengthInSeconds();
        TTBoolean result5 = TTTestFloatEquivalence(return5, TESTDURATIONINSECONDS, true, 0.0000001);

        TTTestAssertion("reports the correct duration in seconds",
                        result5,
                        testAssertionCount,
                        errorCount);

        if(!result5)
        {
            TTTestLog("Expected a value of %f, but returned value was %f", TESTDURATIONINSECONDS, return5);
        }

        TTTestLog("\n");
		TTTestLog("Testing TTSoundfile Metadata...");

        // TEST 6: reports correct title from metadata
        TTSymbol return6 = soundfile->getTitle();

        TTTestLog("Expected metadata title:");
        TTTestLog(TESTTITLE);
        TTTestLog("Returned metadata title:");
        TTTestLog(return6.c_str());

        // TEST 7: reports correct artist from metadata
        TTSymbol return7 = soundfile->getArtist();

        TTTestLog("Expected metadata artist:");
        TTTestLog(TESTARTIST);
        TTTestLog("Returned metadata artist:");
        TTTestLog(return7.c_str());

        // TEST 8: reports correct title from metadata
        TTSymbol return8 = soundfile->getDate();

        TTTestLog("Expected metadata date:");
        TTTestLog(TESTDATE);
        TTTestLog("Returned metadata date:");
        TTTestLog(return8.c_str());

        // TEST 9: reports correct artist from metadata
        TTSymbol return9 = soundfile->getAnnotation();

        TTTestLog("Expected metadata comment:");
        TTTestLog(TESTANNOTATION);
        TTTestLog("Returned metadata comment:");
        TTTestLog(return9.c_str());

        TTTestLog("\n");
		TTTestLog("Testing peek method on first 10 sample values...");

        TTSampleValue return10;
        TTErr error10;

        for (int channel=0;channel<return2;channel++)
        {
            TTTestLog("Channel %i", channel);
            for (int sample=0;sample<10;sample++)
            {
                error10 = soundfile->peek(sample,channel,return10);
                if (error10 == kTTErrNone)
                {
                    TTTestLog("peek sample %i returned the value %f", sample, return10);
                } else {
                    TTTestLog("peek returned an error for sample %i", sample);
                }
            }
        }

        TTTestLog("\n");
		TTTestLog("Testing peeki between samples 2 & 3...");

        TTSampleValue return11;
        TTErr error11;
        TTFloat64 floatIndex;

        for (int channel=0;channel<return2;channel++)
        {
            TTTestLog("Channel %i", channel);
            for (int sample=0;sample<11;sample++)
            {
                floatIndex = 2 + sample*0.1;

                error11 = soundfile->peeki(floatIndex,channel,return11);
                if (error11 == kTTErrNone)
                {
                    TTTestLog("peek sample %f returned the value %f", floatIndex, return11);
                } else {
                    TTTestLog("peek returned an error for sample %f", floatIndex);
                }
            }
        }

    }

    return TTTestFinish(testAssertionCount, errorCount, returnedTestInfo);
}
<|endoftext|>


<commit_before>#include "PackNode.h"
#include "PackIDMgr.h"
#include "typedef.h"

#include <simp/NodeFactory.h>

namespace esprpacker
{

PackNode::PackNode()
	: m_node_id(-1)
	, m_pkg_id(-1)
{
}

uint32_t PackNode::GetID() const
{
	return simp::NodeID::ComposeID(m_pkg_id, m_node_id);
}

void PackNode::SetFilepath(const std::string& filepath) const
{
	m_filepath = filepath;
}

void PackNode::SetID(const std::string& filepath, bool force_curr) const
{
	PackIDMgr::Instance()->QueryID(filepath, m_pkg_id, m_node_id, m_filepath == SPRITE_FILEPATH, force_curr);
}

}<commit_msg>[FIXED] pack node id<commit_after>#include "PackNode.h"
#include "PackIDMgr.h"
#include "typedef.h"

#include <simp/NodeFactory.h>

namespace esprpacker
{

PackNode::PackNode()
	: m_node_id(-1)
	, m_pkg_id(-1)
{
}

uint32_t PackNode::GetID() const
{
	return simp::NodeID::ComposeID(m_pkg_id, m_node_id);
}

void PackNode::SetFilepath(const std::string& filepath) const
{
	m_filepath = filepath;
}

void PackNode::SetID(const std::string& filepath, bool force_curr) const
{
	PackIDMgr::Instance()->QueryID(filepath, m_pkg_id, m_node_id, m_filepath != SPRITE_FILEPATH, force_curr);
}

}<|endoftext|>


<commit_before>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "FireSimulator.h"
#include "../World.h"
#include "../BlockID.h"
#include "../Defines.h"
#include "../Chunk.h"

// Easy switch for turning on debugging logging:
#if 0
	#define FLOG LOGD
#else
	#define FLOG(...)
#endif

#define MAX_CHANCE_REPLACE_FUEL 100000
#define MAX_CHANCE_FLAMMABILITY 100000

static const struct
{
	int x, y, z;
} gCrossCoords[] =
{
	{ 1, 0,  0},
	{-1, 0,  0},
	{ 0, 0,  1},
	{ 0, 0, -1},
} ;

static const struct
{
	int x, y, z;
} gNeighborCoords[] =
{
	{ 1,  0,  0},
	{-1,  0,  0},
	{ 0,  1,  0},
	{ 0, -1,  0},
	{ 0,  0,  1},
	{ 0,  0, -1},
} ;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireSimulator:

cFireSimulator::cFireSimulator(cWorld & a_World, cIniFile & a_IniFile) :
	cSimulator(a_World)
{
	// Read params from the ini file:
	m_BurnStepTimeFuel    = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeFuel",     500);
	m_BurnStepTimeNonfuel = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeNonfuel",  100);
	m_Flammability        = a_IniFile.GetValueSetI("FireSimulator", "Flammability",          50);
	m_ReplaceFuelChance   = a_IniFile.GetValueSetI("FireSimulator", "ReplaceFuelChance",  50000);
}

cFireSimulator::~cFireSimulator()
{
}

void cFireSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
	cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData();

	int NumMSecs = (int)a_Dt;
	for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();)
	{
		int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z);
		BLOCKTYPE BlockType = a_Chunk->GetBlock(idx);

		if (!IsAllowedBlock(BlockType))
		{
			// The block is no longer eligible (not a fire block anymore; a player probably placed a block over the fire)
			FLOG("FS: Removing block {%d, %d, %d}",
				itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
			);
			itr = Data.erase(itr);
			continue;
		}

		// Try to spread the fire:
		TrySpreadFire(a_Chunk, itr->x, itr->y, itr->z);

		itr->Data -= NumMSecs;
		if (itr->Data >= 0)
		{
			// Not yet, wait for it longer
			++itr;
			continue;
		}

		// Burn out the fire one step by increasing the meta:
		/*
		FLOG("FS: Fire at {%d, %d, %d} is stepping",
			itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
		);
		*/
		NIBBLETYPE BlockMeta = a_Chunk->GetMeta(idx);
		if (BlockMeta == 0x0f)
		{
			// The fire burnt out completely
			FLOG("FS: Fire at {%d, %d, %d} burnt out, removing the fire block",
				itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
			);
			a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0);
			RemoveFuelNeighbors(a_Chunk, itr->x, itr->y, itr->z);
			itr = Data.erase(itr);
			continue;
		}
		a_Chunk->SetMeta(idx, BlockMeta + 1);
		itr->Data = GetBurnStepTime(a_Chunk, itr->x, itr->y, itr->z);  // TODO: Add some randomness into this
	}  // for itr - Data[]
}

bool cFireSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
{
	return (a_BlockType == E_BLOCK_FIRE);
}

bool cFireSimulator::IsFuel(BLOCKTYPE a_BlockType)
{
	switch (a_BlockType)
	{
		case E_BLOCK_PLANKS:
		case E_BLOCK_LEAVES:
		case E_BLOCK_LOG:
		case E_BLOCK_WOOL:
		case E_BLOCK_BOOKCASE:
		case E_BLOCK_FENCE:
		case E_BLOCK_TNT:
		case E_BLOCK_VINES:
		{
			return true;
		}
	}
	return false;
}

bool cFireSimulator::IsForever(BLOCKTYPE a_BlockType)
{
	return (a_BlockType == E_BLOCK_NETHERRACK);
}

void cFireSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
{
	if ((a_Chunk == NULL) || !a_Chunk->IsValid())
	{
		return;
	}

	int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
	int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
	BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
	if (!IsAllowedBlock(BlockType))
	{
		return;
	}

	// Check for duplicates:
	cFireSimulatorChunkData & ChunkData = a_Chunk->GetFireSimulatorData();
	for (cCoordWithIntList::iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr)
	{
		if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
		{
			// Already present, skip adding
			return;
		}
	}  // for itr - ChunkData[]

	FLOG("FS: Adding block {%d, %d, %d}", a_BlockX, a_BlockY, a_BlockZ);
	ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ, 100));
}

int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
	bool IsBlockBelowSolid = false;
	if (a_RelY > 0)
	{
		BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
		if (IsForever(BlockBelow))
		{
			// Is burning atop of netherrack, burn forever (re-check in 10 sec)
			return 10000;
		}
		if (IsFuel(BlockBelow))
		{
			return m_BurnStepTimeFuel;
		}
		IsBlockBelowSolid = g_BlockIsSolid[BlockBelow];
	}

	for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
	{
		BLOCKTYPE  BlockType;
		NIBBLETYPE BlockMeta;
		if (a_Chunk->UnboundedRelGetBlock(a_RelX + gCrossCoords[i].x, a_RelY, a_RelZ + gCrossCoords[i].z, BlockType, BlockMeta))
		{
			if (IsFuel(BlockType))
			{
				return m_BurnStepTimeFuel;
			}
		}
	}  // for i - gCrossCoords[]

	if (!IsBlockBelowSolid && (a_RelY >= 0))
	{
		// Checked through everything, nothing was flammable
		// If block below isn't solid, we can't have fire, it would be a non-fueled fire
		// SetBlock just to make sure fire doesn't spawn
		a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
		return 0;
	}
	return m_BurnStepTimeNonfuel;
}

void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
	/*
	if (m_World.GetTickRandomNumber(10000) > 100)
	{
		// Make the chance to spread 100x smaller
		return;
	}
	*/

	for (int x = a_RelX - 1; x <= a_RelX + 1; x++)
	{
		for (int z = a_RelZ - 1; z <= a_RelZ + 1; z++)
		{
			for (int y = a_RelY - 1; y <= a_RelY + 2; y++)  // flames spread up one more block than around
			{
				// No need to check the coords for equality with the parent block,
				// it cannot catch fire anyway (because it's not an air block)

				if (m_World.GetTickRandomNumber(MAX_CHANCE_FLAMMABILITY) > m_Flammability)
				{
					continue;
				}

				// Start the fire in the neighbor {x, y, z}
				/*
				FLOG("FS: Trying to start fire at {%d, %d, %d}.",
					x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width
				);
				*/
				if (CanStartFireInBlock(a_Chunk, x, y, z))
				{
					FLOG("FS: Starting new fire at {%d, %d, %d}.",
						x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width
					);
					a_Chunk->UnboundedRelSetBlock(x, y, z, E_BLOCK_FIRE, 0);
				}
			}  // for y
		}  // for z
	}  // for x
}

void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
	for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++)
	{
		BLOCKTYPE  BlockType;
		NIBBLETYPE BlockMeta;
		if (!a_Chunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta))
		{
			// Neighbor not accessible, ignore it
			continue;
		}
		if (!IsFuel(BlockType))
		{
			continue;
		}
		bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance);
		a_Chunk->UnboundedRelSetBlock(
			a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z,
			ShouldReplaceFuel ? E_BLOCK_FIRE : E_BLOCK_AIR, 0
		);
	}  // for i - Coords[]
}

bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ)
{
	BLOCKTYPE  BlockType;
	NIBBLETYPE BlockMeta;
	if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
	{
		// The chunk is not accessible
		return false;
	}

	if (BlockType != E_BLOCK_AIR)
	{
		// Only an air block can be replaced by a fire block
		return false;
	}

	for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++)
	{
		if (!a_NearChunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta))
		{
			// Neighbor inaccessible, skip it while evaluating
			continue;
		}
		if (IsFuel(BlockType))
		{
			return true;
		}
	}  // for i - Coords[]
	return false;
}

<commit_msg>Fire no longer goes out when on top of nether rack<commit_after>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "FireSimulator.h"
#include "../World.h"
#include "../BlockID.h"
#include "../Defines.h"
#include "../Chunk.h"

// Easy switch for turning on debugging logging:
#if 0
	#define FLOG LOGD
#else
	#define FLOG(...)
#endif

#define MAX_CHANCE_REPLACE_FUEL 100000
#define MAX_CHANCE_FLAMMABILITY 100000

static const struct
{
	int x, y, z;
} gCrossCoords[] =
{
	{ 1, 0,  0},
	{-1, 0,  0},
	{ 0, 0,  1},
	{ 0, 0, -1},
} ;

static const struct
{
	int x, y, z;
} gNeighborCoords[] =
{
	{ 1,  0,  0},
	{-1,  0,  0},
	{ 0,  1,  0},
	{ 0, -1,  0},
	{ 0,  0,  1},
	{ 0,  0, -1},
} ;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireSimulator:

cFireSimulator::cFireSimulator(cWorld & a_World, cIniFile & a_IniFile) :
	cSimulator(a_World)
{
	// Read params from the ini file:
	m_BurnStepTimeFuel    = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeFuel",     500);
	m_BurnStepTimeNonfuel = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeNonfuel",  100);
	m_Flammability        = a_IniFile.GetValueSetI("FireSimulator", "Flammability",          50);
	m_ReplaceFuelChance   = a_IniFile.GetValueSetI("FireSimulator", "ReplaceFuelChance",  50000);
}

cFireSimulator::~cFireSimulator()
{
}

void cFireSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
	cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData();

	int NumMSecs = (int)a_Dt;
	for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();)
	{
		int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z);
		int idb = cChunkDef::MakeIndexNoCheck(itr->x, itr->y - 1, itr->z);
		BLOCKTYPE BlockType = a_Chunk->GetBlock(idx);
		BLOCKTYPE Burnee = a_Chunk->GetBlock(idb);

		if (!IsAllowedBlock(BlockType))
		{
			// The block is no longer eligible (not a fire block anymore; a player probably placed a block over the fire)
			FLOG("FS: Removing block {%d, %d, %d}",
				itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
			);
			itr = Data.erase(itr);
			continue;
		}

		// Try to spread the fire:
		TrySpreadFire(a_Chunk, itr->x, itr->y, itr->z);

		itr->Data -= NumMSecs;
		if (itr->Data >= 0)
		{
			// Not yet, wait for it longer
			++itr;
			continue;
		}

		// Burn out the fire one step by increasing the meta:
		/*
		FLOG("FS: Fire at {%d, %d, %d} is stepping",
			itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
		);
		*/
		NIBBLETYPE BlockMeta = a_Chunk->GetMeta(idx);
		if (BlockMeta == 0x0f)
		{
			// The fire burnt out completely
			FLOG("FS: Fire at {%d, %d, %d} burnt out, removing the fire block",
				itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
			);
			a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0);
			RemoveFuelNeighbors(a_Chunk, itr->x, itr->y, itr->z);
			itr = Data.erase(itr);
			continue;
		}
		if(Burnee != E_BLOCK_NETHERRACK)
		{
			a_Chunk->SetMeta(idx, BlockMeta + 1);
		}
		itr->Data = GetBurnStepTime(a_Chunk, itr->x, itr->y, itr->z);  // TODO: Add some randomness into this
	}  // for itr - Data[]
}

bool cFireSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
{
	return (a_BlockType == E_BLOCK_FIRE);
}

bool cFireSimulator::IsFuel(BLOCKTYPE a_BlockType)
{
	switch (a_BlockType)
	{
		case E_BLOCK_PLANKS:
		case E_BLOCK_LEAVES:
		case E_BLOCK_LOG:
		case E_BLOCK_WOOL:
		case E_BLOCK_BOOKCASE:
		case E_BLOCK_FENCE:
		case E_BLOCK_TNT:
		case E_BLOCK_VINES:
		{
			return true;
		}
	}
	return false;
}

bool cFireSimulator::IsForever(BLOCKTYPE a_BlockType)
{
	return (a_BlockType == E_BLOCK_NETHERRACK);
}

void cFireSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
{
	if ((a_Chunk == NULL) || !a_Chunk->IsValid())
	{
		return;
	}

	int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
	int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
	BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
	if (!IsAllowedBlock(BlockType))
	{
		return;
	}

	// Check for duplicates:
	cFireSimulatorChunkData & ChunkData = a_Chunk->GetFireSimulatorData();
	for (cCoordWithIntList::iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr)
	{
		if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
		{
			// Already present, skip adding
			return;
		}
	}  // for itr - ChunkData[]

	FLOG("FS: Adding block {%d, %d, %d}", a_BlockX, a_BlockY, a_BlockZ);
	ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ, 100));
}

int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
	bool IsBlockBelowSolid = false;
	if (a_RelY > 0)
	{
		BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
		if (IsForever(BlockBelow))
		{
			// Is burning atop of netherrack, burn forever (re-check in 10 sec)
			return 10000;
		}
		if (IsFuel(BlockBelow))
		{
			return m_BurnStepTimeFuel;
		}
		IsBlockBelowSolid = g_BlockIsSolid[BlockBelow];
	}

	for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
	{
		BLOCKTYPE  BlockType;
		NIBBLETYPE BlockMeta;
		if (a_Chunk->UnboundedRelGetBlock(a_RelX + gCrossCoords[i].x, a_RelY, a_RelZ + gCrossCoords[i].z, BlockType, BlockMeta))
		{
			if (IsFuel(BlockType))
			{
				return m_BurnStepTimeFuel;
			}
		}
	}  // for i - gCrossCoords[]

	if (!IsBlockBelowSolid && (a_RelY >= 0))
	{
		// Checked through everything, nothing was flammable
		// If block below isn't solid, we can't have fire, it would be a non-fueled fire
		// SetBlock just to make sure fire doesn't spawn
		a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
		return 0;
	}
	return m_BurnStepTimeNonfuel;
}

void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
	/*
	if (m_World.GetTickRandomNumber(10000) > 100)
	{
		// Make the chance to spread 100x smaller
		return;
	}
	*/

	for (int x = a_RelX - 1; x <= a_RelX + 1; x++)
	{
		for (int z = a_RelZ - 1; z <= a_RelZ + 1; z++)
		{
			for (int y = a_RelY - 1; y <= a_RelY + 2; y++)  // flames spread up one more block than around
			{
				// No need to check the coords for equality with the parent block,
				// it cannot catch fire anyway (because it's not an air block)

				if (m_World.GetTickRandomNumber(MAX_CHANCE_FLAMMABILITY) > m_Flammability)
				{
					continue;
				}

				// Start the fire in the neighbor {x, y, z}
				/*
				FLOG("FS: Trying to start fire at {%d, %d, %d}.",
					x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width
				);
				*/
				if (CanStartFireInBlock(a_Chunk, x, y, z))
				{
					FLOG("FS: Starting new fire at {%d, %d, %d}.",
						x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width
					);
					a_Chunk->UnboundedRelSetBlock(x, y, z, E_BLOCK_FIRE, 0);
				}
			}  // for y
		}  // for z
	}  // for x
}

void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
	for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++)
	{
		BLOCKTYPE  BlockType;
		NIBBLETYPE BlockMeta;
		if (!a_Chunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta))
		{
			// Neighbor not accessible, ignore it
			continue;
		}
		if (!IsFuel(BlockType))
		{
			continue;
		}
		bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance);
		a_Chunk->UnboundedRelSetBlock(
			a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z,
			ShouldReplaceFuel ? E_BLOCK_FIRE : E_BLOCK_AIR, 0
		);
	}  // for i - Coords[]
}

bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ)
{
	BLOCKTYPE  BlockType;
	NIBBLETYPE BlockMeta;
	if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
	{
		// The chunk is not accessible
		return false;
	}

	if (BlockType != E_BLOCK_AIR)
	{
		// Only an air block can be replaced by a fire block
		return false;
	}

	for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++)
	{
		if (!a_NearChunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta))
		{
			// Neighbor inaccessible, skip it while evaluating
			continue;
		}
		if (IsFuel(BlockType))
		{
			return true;
		}
	}  // for i - Coords[]
	return false;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <sstream>
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
#include "dict.h"
#include "dictglobal.h"

using Dict = std::unordered_map<std::string, std::string>;
using IdentificatorType = unsigned long;

namespace {
    #ifndef NDEBUG
        const bool debug = false;
    #else
        const bool debug = true;
    #endif

    IdentificatorType dictCounter = 0;

    std::map<IdentificatorType, Dict>& dicts() {
        static std::map<IdentificatorType, Dict> dicts;

        return dicts;
    }

    std::string parse_char_param(const char* param) {
        if (param != NULL) {
            std::string paramStr(param);
            return "\"" + paramStr + "\"";
        }
        else
            return "NULL";
    }

    void function_called_msg(std::string funcName,
                                    std::string params) {
        if (debug)
            std::cerr << funcName << "(" << params << ")" << std::endl;
    }

    void dict_new_msg(IdentificatorType id) {
        if (debug)
            std::cerr << "dict_new: dict " << id
                      << " has been created" << std::endl;
    }

    void dict_delete_success_msg(IdentificatorType id) {
        if (debug)
            std::cerr << "dict_delete: dict " << id
                      << " has been deleted" << std::endl;
    }

    void dict_delete_error_msg() {
        if (debug)
            std::cerr << "dict_delete: an attempt to remove the Global Dictionary"
                      << std::endl;
    }

    void dict_description_msg(IdentificatorType id) {
        if (debug) {
            if (id != dict_global())
                std::cerr << "dict " << id;
            else
                std::cerr << "the Global Dictionary";
        }
    }

    void dict_size_msg(IdentificatorType id, size_t size) {
        if (debug) {
            std::cerr << "dict_size: ";

            dict_description_msg(id);

            std::cerr << " contains " << size << " element(s)" << std::endl;
        }
    }

    void dict_insert_success_msg(IdentificatorType id,
                                        std::string key,
                                        std::string value) {
        if (debug) {
            std::cerr << "dict_insert: ";

            dict_description_msg(id);

            std::cerr << ", the pair (" << key << ", " << value << ")"
                      << "has been inserted" << std::endl;
        }
    }

    void dict_insert_error_msg(IdentificatorType id, std::string param) {
        if (debug)
            std::cerr << "dict_insert: dict " << id
                      << " an attempt to insert NULL " << param << std::endl;
    }

    void dict_not_found_msg(std::string funcName, IdentificatorType id) {
        if (debug)
            std::cerr << funcName << ": dict " << id << " does not exist" << "\n";
    }

    void key_not_found_msg(std::string funcName,
                                  IdentificatorType id,
                                  const char* key) {
        if (debug)
            std::cerr << funcName << ": dict " << id
                      << " does not contain the key " << key << "\"\n";
    }

    void key_removed_msg(std::string funcName,
                                IdentificatorType id,
                                const char* key) {
        if (debug)
            std::cerr << funcName << ": dict " << id
                      << ", the key \"" << key << "\" has been removed\n";
    }

    void value_found_msg(std::string funcName,
                                IdentificatorType id,
                                const char* key,
                                std::string value) {
        if (debug)
            std::cerr << funcName << ": dict " << id
                      << ", the key \"" << key
                      << "\" has the value \"" << value << "\"\n";
    }

    void dict_copied_msg(std::string funcName,
                                IdentificatorType src_id,
                                IdentificatorType dst_id) {
        if (debug)
            std::cerr << funcName << ": dict " << src_id
                      << " has been copied into dict " << dst_id << "\n";
    }

    void search_global_dict_msg(std::string funcName) {
        if (debug)
            std::cerr << funcName << ": looking up the Global Dictionary\n";
    }

    void dict_cleared_msg(std::string funcName, IdentificatorType id) {
        if (debug)
            std::cerr << funcName << ": dict " << id << " has been cleared\n";
    }
}

IdentificatorType dict_new() {
    function_called_msg("dict_new", "");
    dicts().insert(std::make_pair(++dictCounter, Dict()));

    dict_new_msg(dictCounter);

    return dictCounter;
}

void dict_delete(unsigned long id) {
    std::stringstream ss;
    ss << id;
    function_called_msg("dict_delete", ss.str());

    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        if (id == dict_global()) {
            dict_delete_error_msg();
        }
        else {
            dicts().erase(id);
            dict_delete_success_msg(id);
        }
    }
}

size_t dict_size(unsigned long id) {
    std::stringstream ss;
    ss << id;
    function_called_msg("dict_size", ss.str());

    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        size_t dictSize = dictionaryIt->second.size();

        dict_size_msg(id, dictSize);

        return dictSize;
    }
    else {
        dict_not_found_msg("dict_size", id);
        return 0;
    }
}

void dict_insert(unsigned long id, const char* key, const char* value) {
    std::stringstream ss;
    std::string keyDescription, valueDescription;

    keyDescription = parse_char_param(key);
    valueDescription = parse_char_param(value);
    ss << id << ", " << keyDescription << ", " << valueDescription;

    function_called_msg("dict_insert", ss.str());

    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        if (key == NULL) {
            dict_insert_error_msg(id, "key");
        }
        else if (value == NULL) {
            dict_insert_error_msg(id, "value");
        }
        else {
            // TODO: check if global dict (size constraint!)
            std::string keyStr(key);
            std::string valueStr(value);

            dictionaryIt->second.insert(std::make_pair(keyStr, valueStr));
            dict_insert_success_msg(id, keyDescription, valueDescription);
        }
    }
    else
        dict_not_found_msg("dict_insert", id);
}

void dict_remove(IdentificatorType id, const char* key) {
    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        if (dictionaryIt->second.erase(key) > 0) {
            key_removed_msg("dict_remove", id, key);
        }
        else {
            key_not_found_msg("dict_remove", id, key);
        }
    }
    else {
        dict_not_found_msg("dict_remove", id);
    }
}

const char* dict_find(IdentificatorType id, const char* key) {
    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        auto stringIt = dictionaryIt->second.find(key);
        if (stringIt != dictionaryIt->second.end()) {
            value_found_msg("dict_find", id, key, stringIt->second);

            return stringIt->second.c_str();
        }
        else {
            key_not_found_msg("dict_find", id, key);
        }
    }
    else {
        dict_not_found_msg("dict_find", id);
    }

    search_global_dict_msg("dict_find");

    auto stringIt = dicts().at(dict_global()).find(key);
    if (stringIt != dicts().at(dict_global()).end()) {
        value_found_msg("dict_find", dict_global(), key, stringIt->second);

        return stringIt->second.c_str();
    }
    else {
        key_not_found_msg("dict_find", dict_global(), key);
    }

    return NULL;
}

void dict_clear(IdentificatorType id) {
    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        dict_cleared_msg("dict_clear", id);

        dictionaryIt->second.clear();
    }
    else {
        dict_not_found_msg("dict_clear", id);
    }
}

void dict_copy(IdentificatorType src_id, IdentificatorType dst_id) {
    auto srcDictionaryIt = dicts().find(src_id);
    auto dstDictionaryIt = dicts().find(dst_id);
    if (srcDictionaryIt != dicts().end() &&
        dstDictionaryIt != dicts().end()) {
        // do not copy if destination dictionary is dictglobal and amount of keys exceeds size

        // copy contents
    }
}
<commit_msg>Deutsch -> English<commit_after>#include <iostream>
#include <sstream>
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
#include "dict.h"
#include "dictglobal.h"

using Dict = std::unordered_map<std::string, std::string>;
using IdentifierType = unsigned long;

namespace {
    #ifndef NDEBUG
        const bool debug = false;
    #else
        const bool debug = true;
    #endif

    IdentifierType dictCounter = 0;

    std::map<IdentifierType, Dict>& dicts() {
        static std::map<IdentifierType, Dict> dicts;

        return dicts;
    }

    std::string parse_char_param(const char* param) {
        if (param != NULL) {
            std::string paramStr(param);
            return "\"" + paramStr + "\"";
        }
        else
            return "NULL";
    }

    void function_called_msg(std::string funcName,
                                    std::string params) {
        if (debug)
            std::cerr << funcName << "(" << params << ")" << std::endl;
    }

    void dict_new_msg(IdentifierType id) {
        if (debug)
            std::cerr << "dict_new: dict " << id
                      << " has been created" << std::endl;
    }

    void dict_delete_success_msg(IdentifierType id) {
        if (debug)
            std::cerr << "dict_delete: dict " << id
                      << " has been deleted" << std::endl;
    }

    void dict_delete_error_msg() {
        if (debug)
            std::cerr << "dict_delete: an attempt to remove the Global Dictionary"
                      << std::endl;
    }

    void dict_description_msg(IdentifierType id) {
        if (debug) {
            if (id != dict_global())
                std::cerr << "dict " << id;
            else
                std::cerr << "the Global Dictionary";
        }
    }

    void dict_size_msg(IdentifierType id, size_t size) {
        if (debug) {
            std::cerr << "dict_size: ";

            dict_description_msg(id);

            std::cerr << " contains " << size << " element(s)" << std::endl;
        }
    }

    void dict_insert_success_msg(IdentifierType id,
                                        std::string key,
                                        std::string value) {
        if (debug) {
            std::cerr << "dict_insert: ";

            dict_description_msg(id);

            std::cerr << ", the pair (" << key << ", " << value << ")"
                      << "has been inserted" << std::endl;
        }
    }

    void dict_insert_error_msg(IdentifierType id, std::string param) {
        if (debug)
            std::cerr << "dict_insert: dict " << id
                      << " an attempt to insert NULL " << param << std::endl;
    }

    void dict_not_found_msg(std::string funcName, IdentifierType id) {
        if (debug)
            std::cerr << funcName << ": dict " << id << " does not exist" << "\n";
    }

    void key_not_found_msg(std::string funcName,
                                  IdentifierType id,
                                  const char* key) {
        if (debug)
            std::cerr << funcName << ": dict " << id
                      << " does not contain the key " << key << "\"\n";
    }

    void key_removed_msg(std::string funcName,
                                IdentifierType id,
                                const char* key) {
        if (debug)
            std::cerr << funcName << ": dict " << id
                      << ", the key \"" << key << "\" has been removed\n";
    }

    void value_found_msg(std::string funcName,
                                IdentifierType id,
                                const char* key,
                                std::string value) {
        if (debug)
            std::cerr << funcName << ": dict " << id
                      << ", the key \"" << key
                      << "\" has the value \"" << value << "\"\n";
    }

    void dict_copied_msg(std::string funcName,
                                IdentifierType src_id,
                                IdentifierType dst_id) {
        if (debug)
            std::cerr << funcName << ": dict " << src_id
                      << " has been copied into dict " << dst_id << "\n";
    }

    void search_global_dict_msg(std::string funcName) {
        if (debug)
            std::cerr << funcName << ": looking up the Global Dictionary\n";
    }

    void dict_cleared_msg(std::string funcName, IdentifierType id) {
        if (debug)
            std::cerr << funcName << ": dict " << id << " has been cleared\n";
    }
}

IdentifierType dict_new() {
    function_called_msg("dict_new", "");
    dicts().insert(std::make_pair(++dictCounter, Dict()));

    dict_new_msg(dictCounter);

    return dictCounter;
}

void dict_delete(unsigned long id) {
    std::stringstream ss;
    ss << id;
    function_called_msg("dict_delete", ss.str());

    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        if (id == dict_global()) {
            dict_delete_error_msg();
        }
        else {
            dicts().erase(id);
            dict_delete_success_msg(id);
        }
    }
}

size_t dict_size(unsigned long id) {
    std::stringstream ss;
    ss << id;
    function_called_msg("dict_size", ss.str());

    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        size_t dictSize = dictionaryIt->second.size();

        dict_size_msg(id, dictSize);

        return dictSize;
    }
    else {
        dict_not_found_msg("dict_size", id);
        return 0;
    }
}

void dict_insert(unsigned long id, const char* key, const char* value) {
    std::stringstream ss;
    std::string keyDescription, valueDescription;

    keyDescription = parse_char_param(key);
    valueDescription = parse_char_param(value);
    ss << id << ", " << keyDescription << ", " << valueDescription;

    function_called_msg("dict_insert", ss.str());

    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        if (key == NULL) {
            dict_insert_error_msg(id, "key");
        }
        else if (value == NULL) {
            dict_insert_error_msg(id, "value");
        }
        else {
            // TODO: check if global dict (size constraint!)
            std::string keyStr(key);
            std::string valueStr(value);

            dictionaryIt->second.insert(std::make_pair(keyStr, valueStr));
            dict_insert_success_msg(id, keyDescription, valueDescription);
        }
    }
    else
        dict_not_found_msg("dict_insert", id);
}

void dict_remove(IdentifierType id, const char* key) {
    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        if (dictionaryIt->second.erase(key) > 0) {
            key_removed_msg("dict_remove", id, key);
        }
        else {
            key_not_found_msg("dict_remove", id, key);
        }
    }
    else {
        dict_not_found_msg("dict_remove", id);
    }
}

const char* dict_find(IdentifierType id, const char* key) {
    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        auto stringIt = dictionaryIt->second.find(key);
        if (stringIt != dictionaryIt->second.end()) {
            value_found_msg("dict_find", id, key, stringIt->second);

            return stringIt->second.c_str();
        }
        else {
            key_not_found_msg("dict_find", id, key);
        }
    }
    else {
        dict_not_found_msg("dict_find", id);
    }

    search_global_dict_msg("dict_find");

    auto stringIt = dicts().at(dict_global()).find(key);
    if (stringIt != dicts().at(dict_global()).end()) {
        value_found_msg("dict_find", dict_global(), key, stringIt->second);

        return stringIt->second.c_str();
    }
    else {
        key_not_found_msg("dict_find", dict_global(), key);
    }

    return NULL;
}

void dict_clear(IdentifierType id) {
    auto dictionaryIt = dicts().find(id);
    if (dictionaryIt != dicts().end()) {
        dict_cleared_msg("dict_clear", id);

        dictionaryIt->second.clear();
    }
    else {
        dict_not_found_msg("dict_clear", id);
    }
}

void dict_copy(IdentifierType src_id, IdentifierType dst_id) {
    auto srcDictionaryIt = dicts().find(src_id);
    auto dstDictionaryIt = dicts().find(dst_id);
    if (srcDictionaryIt != dicts().end() &&
        dstDictionaryIt != dicts().end()) {
        // do not copy if destination dictionary is dictglobal and amount of keys exceeds size

        // copy contents
    }
}
<|endoftext|>


<commit_before>//
// Created by Yihung Lee on 10/31/17.
//

#include "gtest/gtest.h"
#include "LeetCodeHeaders.hpp"

ArrayQuiz aq;

TEST (ArrayTest, findDuplicate) {
    std::vector<int> vec{1, 2, 3, 4, 5, 6, 5, 7, 8, 9};
    EXPECT_EQ(5, aq.findDuplicate(vec));
}

TEST (ArrayTest, lengthOfLongestSubstring) {
    cout << "\nLongest Substring Without Repeating Characters\n";
    string s1 = "abcabcbb";
    cout << aq.lengthOfLongestSubstring(s1) << endl;
//    string s2 = "bbbbb";
//    cout << aq.lengthOfLongestSubstring(s2) << endl;
//    string s3 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~";
//    cout << aq.lengthOfLongestSubstring(s3) << endl;
}<commit_msg>test case<commit_after>//
// Created by Yihung Lee on 10/31/17.
//

#include "gtest/gtest.h"
#include "LeetCodeHeaders.hpp"

ArrayQuiz aq;

TEST (ArrayTest, findDuplicate) {
    std::vector<int> vec{1, 2, 3, 4, 5, 6, 5, 7, 8, 9};
    EXPECT_EQ(5, aq.findDuplicate(vec));
}

TEST (ArrayTest, lengthOfLongestSubstring) {
    cout << "\nLongest Substring Without Repeating Characters\n";
    string s1 = "abcabcbb";
    EXPECT_EQ(3, aq.lengthOfLongestSubstring(s1));
    string s2 = "bbbbb";
    EXPECT_EQ(1, aq.lengthOfLongestSubstring(s2));
    string s3 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~";
    EXPECT_EQ(86, aq.lengthOfLongestSubstring(s3));
}<|endoftext|>