/*
 * PtokaX - hub server for Direct Connect peer to peer network.

 * Copyright (C) 2004-2022  Petr Kozelka, PPK at PtokaX dot org

 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * as published by the Free Software Foundation.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "stdinc.h"
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "PXBReader.h"
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "ServerManager.h"
#include "utility.h"
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#ifdef _WIN32
#pragma hdrstop
#endif
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

PXBReader::PXBReader() : m_pFile(NULL), m_pActualPosition(NULL), m_szRemainingSize(0), m_ui8AllocatedSize(0), m_bFullRead(false), m_pItemDatas(NULL), m_ui16ItemLengths(NULL), m_sItemIdentifiers(NULL), m_ui8ItemValues(NULL)
{
	// ...
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

PXBReader::~PXBReader()
{
	free(m_pItemDatas);
	free(m_ui16ItemLengths);
	free(m_sItemIdentifiers);
	free(m_ui8ItemValues);

	if (m_pFile != NULL)
	{
		fclose(m_pFile);
		m_pFile = NULL;
	}
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool PXBReader::OpenFileRead(const char * sFilename, const uint8_t ui8SubItems)
{
	if (PrepareArrays(ui8SubItems) == false)
	{
		return false;
	}

	m_pFile = fopen(sFilename, "rb");

	if (m_pFile == NULL)
	{
		return false;
	}

	fseek(m_pFile, 0, SEEK_END);
	long lFileLen = ftell(m_pFile);

	if (lFileLen <= 0)
	{
		return false;
	}

	fseek(m_pFile, 0, SEEK_SET);

	m_szRemainingSize = PTOKAX_GLOBAL_BUFF_SIZE;

	if ((size_t)lFileLen < m_szRemainingSize)
	{
		m_szRemainingSize = lFileLen;

		m_bFullRead = true;
	}

	if (fread(ServerManager::m_pGlobalBuffer, 1, m_szRemainingSize, m_pFile) != m_szRemainingSize)
	{
		return false;
	}

	m_pActualPosition = ServerManager::m_pGlobalBuffer;

	return true;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void PXBReader::ReadNextFilePart()
{
	memmove(ServerManager::m_pGlobalBuffer, m_pActualPosition, m_szRemainingSize);

	size_t szReadSize = fread(ServerManager::m_pGlobalBuffer + m_szRemainingSize, 1, PTOKAX_GLOBAL_BUFF_SIZE - m_szRemainingSize, m_pFile);

	if (szReadSize != (PTOKAX_GLOBAL_BUFF_SIZE - m_szRemainingSize))
	{
		m_bFullRead = true;
	}

	m_pActualPosition = ServerManager::m_pGlobalBuffer;
	m_szRemainingSize += szReadSize;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool PXBReader::ReadNextItem(const uint16_t * pExpectedIdentificators, const uint8_t ui8ExpectedSubItems, const uint8_t ui8ExtraSubItems/* = 0*/)
{
	if (m_szRemainingSize == 0)
	{
		return false;
	}

	memset(m_pItemDatas, 0, m_ui8AllocatedSize * sizeof(void *));
	memset(m_ui16ItemLengths, 0, m_ui8AllocatedSize * sizeof(uint16_t));

	if (m_szRemainingSize < 4)
	{
		if (m_bFullRead == true)
		{
			return false;
		}
		else     // read next part of file
		{
			ReadNextFilePart();

			if (m_szRemainingSize < 4)
			{
				return false;
			}
		}
	}

	uint32_t ui32ItemSize = ntohl(*((uint32_t *)m_pActualPosition));

	if (ui32ItemSize > m_szRemainingSize)
	{
		if (m_bFullRead == true)
		{
			return false;
		}
		else     // read next part of file
		{
			ReadNextFilePart();

			if (ui32ItemSize > m_szRemainingSize)
			{
				return false;
			}
		}
	}

	m_pActualPosition += 4;
	m_szRemainingSize -= 4;
	ui32ItemSize -= 4;

	uint8_t ui8ActualItem = 0;

	uint16_t ui16SubItemSize = 0;

	while (ui32ItemSize > 0)
	{
		ui16SubItemSize = ntohs(*((uint16_t *)m_pActualPosition));

		if (ui16SubItemSize > ui32ItemSize)
		{
			return false;
		}

		if (ui8ActualItem < ui8ExpectedSubItems && pExpectedIdentificators[ui8ActualItem] == *((uint16_t *)(m_pActualPosition + 2)))
		{
			m_ui16ItemLengths[ui8ActualItem] = (ui16SubItemSize - 4);
			m_pItemDatas[ui8ActualItem] = (m_pActualPosition + 4);

			ui8ActualItem++;
		}
		else     // for compatibility with newer versions...
		{
			for (uint8_t ui8i = 0; ui8i < (ui8ExpectedSubItems + ui8ExtraSubItems); ui8i++)
			{
				if (pExpectedIdentificators[ui8i] == *((uint16_t *)(m_pActualPosition + 2)))
				{
					m_ui16ItemLengths[ui8i] = (ui16SubItemSize - 4);
					m_pItemDatas[ui8i] = (m_pActualPosition + 4);

					ui8ActualItem++;
				}
			}
		}

		m_pActualPosition += ui16SubItemSize;
		m_szRemainingSize -= ui16SubItemSize;
		ui32ItemSize -= ui16SubItemSize;
	}

	if (ui8ActualItem != ui8ExpectedSubItems)
	{
		return false;
	}
	else
	{
		return true;
	}
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool PXBReader::OpenFileSave(const char * sFilename, const uint8_t ui8Size)
{
	if (PrepareArrays(ui8Size) == false)
	{
		return false;
	}

	m_pFile = fopen(sFilename, "wb");

	if (m_pFile == NULL)
	{
		return false;
	}

	m_szRemainingSize = PTOKAX_GLOBAL_BUFF_SIZE;

	m_pActualPosition = ServerManager::m_pGlobalBuffer;

	return true;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool PXBReader::WriteNextItem(const uint32_t ui32Length, const uint8_t ui8SubItems)
{
	uint32_t ui32ItemLength = ui32Length + 4 + (4 * ui8SubItems);

	if (ui32ItemLength > m_szRemainingSize)
	{
		fwrite(ServerManager::m_pGlobalBuffer, 1, m_pActualPosition - ServerManager::m_pGlobalBuffer, m_pFile);
		m_pActualPosition = ServerManager::m_pGlobalBuffer;
		m_szRemainingSize = PTOKAX_GLOBAL_BUFF_SIZE;
	}

	(*((uint32_t *)m_pActualPosition)) = htonl(ui32ItemLength);
	m_pActualPosition += 4;
	m_szRemainingSize -= 4;

	for (uint8_t ui8i = 0; ui8i < ui8SubItems; ui8i++)
	{
		(*((uint16_t *)(m_pActualPosition))) = htons(m_ui16ItemLengths[ui8i] + 4);

		m_pActualPosition[2] = m_sItemIdentifiers[(ui8i * 2)];
		m_pActualPosition[3] = m_sItemIdentifiers[(ui8i * 2) + 1];

		switch (m_ui8ItemValues[ui8i])
		{
		case PXB_BYTE:
			m_pActualPosition[4] = (m_pItemDatas[ui8i] == 0 ? '0' : '1');
			break;
		case PXB_TWO_BYTES:
			(*((uint16_t *)(m_pActualPosition + 4))) = htons(*((uint16_t *)m_pItemDatas[ui8i]));
			break;
		case PXB_FOUR_BYTES:
			(*((uint32_t *)(m_pActualPosition + 4))) = htonl(*((uint32_t *)m_pItemDatas[ui8i]));
			break;
		case PXB_EIGHT_BYTES:
			(*((uint64_t *)(m_pActualPosition + 4))) = htobe64(*((uint64_t *)m_pItemDatas[ui8i]));
			break;
		case PXB_STRING:
			memcpy(m_pActualPosition + 4, m_pItemDatas[ui8i], m_ui16ItemLengths[ui8i]);
			break;
		default:
			break;
		}

		m_pActualPosition += m_ui16ItemLengths[ui8i] + 4;
		m_szRemainingSize -= m_ui16ItemLengths[ui8i] + 4;
	}

	return true;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void PXBReader::WriteRemaining()
{
	if ((m_pActualPosition - ServerManager::m_pGlobalBuffer) > 0)
	{
		fwrite(ServerManager::m_pGlobalBuffer, 1, m_pActualPosition - ServerManager::m_pGlobalBuffer, m_pFile);
	}

	fclose(m_pFile);
	m_pFile = NULL;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool PXBReader::PrepareArrays(const uint8_t ui8Size)
{
	m_pItemDatas = (void **)calloc(ui8Size, sizeof(void *));
	if (m_pItemDatas == NULL)
	{
		AppendDebugLog("%s - [MEM] Cannot create m_pItemDatas in PXBReader::PrepareArrays\n");
		return false;
	}

	m_ui16ItemLengths = (uint16_t *)calloc(ui8Size, sizeof(uint16_t));
	if (m_ui16ItemLengths == NULL)
	{
		AppendDebugLog("%s - [MEM] Cannot create m_ui16ItemLengths in PXBReader::PrepareArrays\n");
		return false;
	}

	m_sItemIdentifiers = (char *)calloc(ui8Size, sizeof(char) * 2);
	if (m_sItemIdentifiers == NULL)
	{
		AppendDebugLog("%s - [MEM] Cannot create m_sItemIdentifiers in PXBReader::PrepareArrays\n");
		return false;
	}

	m_ui8ItemValues = (uint8_t *)calloc(ui8Size, sizeof(uint8_t));
	if (m_ui8ItemValues == NULL)
	{
		AppendDebugLog("%s - [MEM] Cannot create m_ui8ItemValues in PXBReader::PrepareArrays\n");
		return false;
	}

	m_ui8AllocatedSize = ui8Size;
	return true;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

V1042 This file is marked with copyleft license, which requires you to open the derived source code.