<commit_before>#include "control.h"

#include "modbusdevice.h"
#include "hardware.h"
#include "plc_lib.h"
#include "powercalc.h"

#include <QTime>
#include <stdio.h>

ControlThread::ControlThread()
{
	wVelFanCoil = 20;
	xUsaPompaCalore = true;

	start();
}

ControlThread::~ControlThread()
{

}

void echo(const char *what, const char *where)
{
	FILE *f = fopen(where, "w");
	fprintf(f, "%s", what);
	fclose(f);
}

void ControlThread::run()
{
	ModbusDevice::openSerial("/dev/ttyUSB0", 38400, 'N', 8, 1);
	InitHardware();

	QTime lastTime = QTime::currentTime();
	int lastSecs = lastTime.hour()*3600 + lastTime.minute()*60 + lastTime.second();

	while(1) {

		static PowerCalc pcProdotta;
		static PowerCalc pcConsumata;

		ReadHardwareInputs();

		Timer::tick();
		QTime now = QTime::currentTime();
		int nowSecs = now.hour()*3600 + now.minute()*60 + now.second();

		if ((nowSecs != lastSecs) && ((nowSecs % SAMPLE_PERIOD_SECS) == 0)) {
			pcProdotta.addSample(HW.Pannelli.wPotenzaProdotta->getValue());
			wPotProdotta = pcProdotta.getCurrentPower();
			wEnergProdotta = pcProdotta.getCurrentEnergy();
			pcConsumata.addSample(HW.Pannelli.wPotenzaConsumata->getValue());
			wPotConsumata = pcConsumata.getCurrentPower();
			wEnergConsumata = pcConsumata.getCurrentEnergy();
			printf("\n");
		}

		if (now.minute() != lastTime.minute()) {
			int prod = pcProdotta.getDeltaSteps();
			int cons = pcConsumata.getDeltaSteps();
			if (prod > cons)
				tTempoAttivo = tTempoAttivo.addSecs(60);
		}

		if (now.hour() != lastTime.hour()) {
			switch (now.hour()) {
			case 0:
				// NEXT DAY!
				pcProdotta.resetTotals();
				pcConsumata.resetTotals();
				tTempoAttivo.setHMS(0,0,0);
				break;
			case 6:
				// wake up display
				echo("160", "/sys/devices/platform/pwm-backlight.0/backlight/pwm-backlight.0/brightness");
				break;
			case 22:
				// set display to sleep brightness
				echo("32", "/sys/devices/platform/pwm-backlight.0/backlight/pwm-backlight.0/brightness");
				break;
			}
		}

		mFields.lock();

		wTemperaturaACS = HW.PompaCalore.wTemperaturaACS->getValue();
		wTemperaturaBoiler = HW.PompaCalore.wTemperaturaBoiler->getValue();
		wTemperaturaAccumulo = HW.Accumulo.wTemperatura->getValue();
		wTemperaturaPannelli = HW.PompaCalore.wTemperaturaPannelli->getValue();

		wTempSoffitta = HW.Ambiente.wTemperaturaSoffitta->getValue();
		wUmidSoffitta = HW.Ambiente.wUmiditaSoffitta->getValue();

		bool risc_acceso = xRiscaldaNotte || xRiscaldaGiorno || xRiscaldaSoffitta;
		HW.Riscaldamento.xChiudiValvola->setValue(!risc_acceso);
		static DelayRiseTimer tStartPompe;
		bool start_pompe = tStartPompe.update(DELAY_SEC(20), risc_acceso);
		HW.Riscaldamento.xStartPompaGiorno->setValue(start_pompe && xRiscaldaGiorno);
		HW.Riscaldamento.xStartPompaNotte->setValue(start_pompe && xRiscaldaNotte);
		HW.Riscaldamento.xStartPompaSoffitta->setValue(start_pompe && xRiscaldaSoffitta);
		HW.Riscaldamento.xStartFanCoilStanzaSoffitta->setValue(start_pompe && xRiscaldaSoffitta);
		HW.Riscaldamento.xStartFanCoilBagnoSoffitta->setValue(start_pompe && xRiscaldaSoffitta);

		// setup valvole pompa calore
		static DelayRiseTimer tResetManValvole;
		bool reset_man_finito = tResetManValvole.update(DELAY_SEC(8), risc_acceso);
		static DelayRiseTimer tSetPosValvolaUV1;
		bool set_pos_uv1_finito = tSetPosValvolaUV1.update(DELAY_MSEC(1900), reset_man_finito);
		HW.PompaCalore.xForzaValvole->setValue(risc_acceso);
		HW.PompaCalore.xForza3VieApri->setValue(risc_acceso && false);
		HW.PompaCalore.xForza3VieChiudi->setValue(risc_acceso && true);
		HW.PompaCalore.xForzaRiscApri->setValue(risc_acceso && !reset_man_finito);
		HW.PompaCalore.xForzaRiscFerma->setValue(set_pos_uv1_finito);

		xResistenzeInUso = xUsaResistenze && (wTemperaturaACS < 800) && (wTemperaturaBoiler < 800);
		HW.PompaCalore.xInserResistenze->setValue(xResistenzeInUso);

		HW.FanCoilCorridoio.xChiudiValvola->setValue(!xFanCoil);
		static DelayRiseTimer tStartFanCoil;
		HW.FanCoilCorridoio.xStartVentilatore->setValue(tStartFanCoil.update(DELAY_SEC(30), xFanCoil));
		HW.FanCoilCorridoio.wLivelloVentilatore->setValue(wVelFanCoil*200);

		bool muovi_serranda = xApriCucina || xChiudiCucina;
		HW.FanCoilCorridoio.xMuoviSerrandaCucina->setValue(muovi_serranda);
		HW.FanCoilCorridoio.xApriSerrandaCucina->setValue(xApriCucina);
		static DelayRiseTimer tStopSerrandaCucina;
		bool stop_serranda = tStopSerrandaCucina.update(xChiudiCucina? DELAY_SEC(30) : DELAY_SEC(13), muovi_serranda);
		if (stop_serranda)  {
			xApriCucina = xChiudiCucina = false;
		}

		// FIXME in estate e' diverso!!
		if (risc_acceso || xFanCoil)
			xPompaCaloreInUso = false;
		else
			xPompaCaloreInUso = xUsaPompaCalore;

		HW.PompaCalore.xStopPompaCalore->setValue(!xPompaCaloreInUso);
//		HW.PompaCalore.xRichiestaCaldo->setValue(risc_acceso || xFanCoil);

		bool zona_attiva = false;
		if (!xUsaPompaCalore) {
			/* caldaia auto */
			zona_attiva |= ((now>QTime(6,0)) && (now<QTime(8,30)));
			zona_attiva |= ((now>QTime(11,0)) && (now<QTime(15,0)));
			zona_attiva |= ((now>QTime(18,0)) && (now<QTime(21,0)));
		}
		zona_attiva |= risc_acceso;
		if (zona_attiva) {
			/* auto mode: caldaia = richiesta acs | bottone */
			if (wTemperaturaACS < 450)
				xCaldaiaInUso = true;
			else if (wTemperaturaACS > 500)
				xCaldaiaInUso = false;
			xCaldaiaInUso |= xUsaCaldaia;
		} else {
			/* man mode: caldaia = bottone */
			xCaldaiaInUso = xUsaCaldaia;
		}

		HW.Caldaia.xAlimenta->setValue(xCaldaiaInUso);
		static DelayRiseTimer tStartCaldaia;
		HW.Caldaia.xStartCaldaia->setValue(tStartCaldaia.update(DELAY_SEC(10), xCaldaiaInUso));
		static DelayFallTimer tStartPompa;
		HW.Caldaia.xStartPompa->setValue(tStartPompa.update(DELAY_SEC(60), HW.Caldaia.xStartCaldaia->getValue()));

		HW.Accumulo.xApriValvola->setValue(xTrasfAccumulo);

		static DelayRiseTimer tStartPompaAccumulo;
		bool ok_start_pompa_accumulo = tStartPompaAccumulo.update(DELAY_MIN(2), xTrasfAccumulo);
		static DelayRiseTimer tDurataPompaAccumulo;
		bool max_durata_pompa_accumulo = tDurataPompaAccumulo.update(DELAY_MIN(60), ok_start_pompa_accumulo);
		HW.Accumulo.xStartPompa->setValue(ok_start_pompa_accumulo && !max_durata_pompa_accumulo);
		xTrasfAccumuloInCorso = xTrasfAccumulo && !max_durata_pompa_accumulo;

		mFields.unlock();
		WriteHardwareOutputs();
		lastTime = now;
		lastSecs = nowSecs;
	}
}

ControlThread &control()
{
	static ControlThread theControl;
	return theControl;
}
<commit_msg>Ripristino richiesta caldo<commit_after>#include "control.h"

#include "modbusdevice.h"
#include "hardware.h"
#include "plc_lib.h"
#include "powercalc.h"

#include <QTime>
#include <stdio.h>

ControlThread::ControlThread()
{
	wVelFanCoil = 20;
	xUsaPompaCalore = true;

	start();
}

ControlThread::~ControlThread()
{

}

void echo(const char *what, const char *where)
{
	FILE *f = fopen(where, "w");
	fprintf(f, "%s", what);
	fclose(f);
}

void ControlThread::run()
{
	ModbusDevice::openSerial("/dev/ttyUSB0", 38400, 'N', 8, 1);
	InitHardware();

	QTime lastTime = QTime::currentTime();
	int lastSecs = lastTime.hour()*3600 + lastTime.minute()*60 + lastTime.second();

	while(1) {

		static PowerCalc pcProdotta;
		static PowerCalc pcConsumata;

		ReadHardwareInputs();

		Timer::tick();
		QTime now = QTime::currentTime();
		int nowSecs = now.hour()*3600 + now.minute()*60 + now.second();

		if ((nowSecs != lastSecs) && ((nowSecs % SAMPLE_PERIOD_SECS) == 0)) {
			pcProdotta.addSample(HW.Pannelli.wPotenzaProdotta->getValue());
			wPotProdotta = pcProdotta.getCurrentPower();
			wEnergProdotta = pcProdotta.getCurrentEnergy();
			pcConsumata.addSample(HW.Pannelli.wPotenzaConsumata->getValue());
			wPotConsumata = pcConsumata.getCurrentPower();
			wEnergConsumata = pcConsumata.getCurrentEnergy();
			printf("\n");
		}

		if (now.minute() != lastTime.minute()) {
			int prod = pcProdotta.getDeltaSteps();
			int cons = pcConsumata.getDeltaSteps();
			if (prod > cons)
				tTempoAttivo = tTempoAttivo.addSecs(60);
		}

		if (now.hour() != lastTime.hour()) {
			switch (now.hour()) {
			case 0:
				// NEXT DAY!
				pcProdotta.resetTotals();
				pcConsumata.resetTotals();
				tTempoAttivo.setHMS(0,0,0);
				break;
			case 6:
				// wake up display
				echo("160", "/sys/devices/platform/pwm-backlight.0/backlight/pwm-backlight.0/brightness");
				break;
			case 22:
				// set display to sleep brightness
				echo("32", "/sys/devices/platform/pwm-backlight.0/backlight/pwm-backlight.0/brightness");
				break;
			}
		}

		mFields.lock();

		wTemperaturaACS = HW.PompaCalore.wTemperaturaACS->getValue();
		wTemperaturaBoiler = HW.PompaCalore.wTemperaturaBoiler->getValue();
		wTemperaturaAccumulo = HW.Accumulo.wTemperatura->getValue();
		wTemperaturaPannelli = HW.PompaCalore.wTemperaturaPannelli->getValue();

		wTempSoffitta = HW.Ambiente.wTemperaturaSoffitta->getValue();
		wUmidSoffitta = HW.Ambiente.wUmiditaSoffitta->getValue();

		bool risc_acceso = xRiscaldaNotte || xRiscaldaGiorno || xRiscaldaSoffitta;
		HW.Riscaldamento.xChiudiValvola->setValue(!risc_acceso);
		static DelayRiseTimer tStartPompe;
		bool start_pompe = tStartPompe.update(DELAY_SEC(20), risc_acceso);
		HW.Riscaldamento.xStartPompaGiorno->setValue(start_pompe && xRiscaldaGiorno);
		HW.Riscaldamento.xStartPompaNotte->setValue(start_pompe && xRiscaldaNotte);
		HW.Riscaldamento.xStartPompaSoffitta->setValue(start_pompe && xRiscaldaSoffitta);
		HW.Riscaldamento.xStartFanCoilStanzaSoffitta->setValue(start_pompe && xRiscaldaSoffitta);
		HW.Riscaldamento.xStartFanCoilBagnoSoffitta->setValue(start_pompe && xRiscaldaSoffitta);

		// setup valvole pompa calore
		static DelayRiseTimer tResetManValvole;
		bool reset_man_finito = tResetManValvole.update(DELAY_SEC(8), risc_acceso);
		static DelayRiseTimer tSetPosValvolaUV1;
		bool set_pos_uv1_finito = tSetPosValvolaUV1.update(DELAY_MSEC(1900), reset_man_finito);
		HW.PompaCalore.xForzaValvole->setValue(risc_acceso);
		HW.PompaCalore.xForza3VieApri->setValue(risc_acceso && false);
		HW.PompaCalore.xForza3VieChiudi->setValue(risc_acceso && true);
		HW.PompaCalore.xForzaRiscApri->setValue(risc_acceso && !reset_man_finito);
		HW.PompaCalore.xForzaRiscFerma->setValue(set_pos_uv1_finito);

		xResistenzeInUso = xUsaResistenze && (wTemperaturaACS < 800) && (wTemperaturaBoiler < 800);
		HW.PompaCalore.xInserResistenze->setValue(xResistenzeInUso);

		HW.FanCoilCorridoio.xChiudiValvola->setValue(!xFanCoil);
		static DelayRiseTimer tStartFanCoil;
		HW.FanCoilCorridoio.xStartVentilatore->setValue(tStartFanCoil.update(DELAY_SEC(30), xFanCoil));
		HW.FanCoilCorridoio.wLivelloVentilatore->setValue(wVelFanCoil*200);

		bool muovi_serranda = xApriCucina || xChiudiCucina;
		HW.FanCoilCorridoio.xMuoviSerrandaCucina->setValue(muovi_serranda);
		HW.FanCoilCorridoio.xApriSerrandaCucina->setValue(xApriCucina);
		static DelayRiseTimer tStopSerrandaCucina;
		bool stop_serranda = tStopSerrandaCucina.update(xChiudiCucina? DELAY_SEC(30) : DELAY_SEC(13), muovi_serranda);
		if (stop_serranda)  {
			xApriCucina = xChiudiCucina = false;
		}

		// FIXME in estate e' diverso!!
		if (risc_acceso || xFanCoil)
			xPompaCaloreInUso = false;
		else
			xPompaCaloreInUso = xUsaPompaCalore;

		HW.PompaCalore.xStopPompaCalore->setValue(!xPompaCaloreInUso);
		HW.PompaCalore.xRichiestaCaldo->setValue(risc_acceso || xFanCoil);

		bool zona_attiva = false;
		if (!xUsaPompaCalore) {
			/* caldaia auto */
			zona_attiva |= ((now>QTime(6,0)) && (now<QTime(8,30)));
			zona_attiva |= ((now>QTime(11,0)) && (now<QTime(15,0)));
			zona_attiva |= ((now>QTime(18,0)) && (now<QTime(21,0)));
		}
		zona_attiva |= risc_acceso;
		if (zona_attiva) {
			/* auto mode: caldaia = richiesta acs | bottone */
			if (wTemperaturaACS < 450)
				xCaldaiaInUso = true;
			else if (wTemperaturaACS > 500)
				xCaldaiaInUso = false;
			xCaldaiaInUso |= xUsaCaldaia;
		} else {
			/* man mode: caldaia = bottone */
			xCaldaiaInUso = xUsaCaldaia;
		}

		HW.Caldaia.xAlimenta->setValue(xCaldaiaInUso);
		static DelayRiseTimer tStartCaldaia;
		HW.Caldaia.xStartCaldaia->setValue(tStartCaldaia.update(DELAY_SEC(10), xCaldaiaInUso));
		static DelayFallTimer tStartPompa;
		HW.Caldaia.xStartPompa->setValue(tStartPompa.update(DELAY_SEC(60), HW.Caldaia.xStartCaldaia->getValue()));

		HW.Accumulo.xApriValvola->setValue(xTrasfAccumulo);

		static DelayRiseTimer tStartPompaAccumulo;
		bool ok_start_pompa_accumulo = tStartPompaAccumulo.update(DELAY_MIN(2), xTrasfAccumulo);
		static DelayRiseTimer tDurataPompaAccumulo;
		bool max_durata_pompa_accumulo = tDurataPompaAccumulo.update(DELAY_MIN(60), ok_start_pompa_accumulo);
		HW.Accumulo.xStartPompa->setValue(ok_start_pompa_accumulo && !max_durata_pompa_accumulo);
		xTrasfAccumuloInCorso = xTrasfAccumulo && !max_durata_pompa_accumulo;

		mFields.unlock();
		WriteHardwareOutputs();
		lastTime = now;
		lastSecs = nowSecs;
	}
}

ControlThread &control()
{
	static ControlThread theControl;
	return theControl;
}
<|endoftext|>


<commit_before>#include "game/ui/general/savemenu.h"
#include "forms/ui.h"
#include "framework/event.h"
#include "framework/framework.h"
#include "game/state/gamestate.h"
#include "game/ui/general/loadingscreen.h"
#include "game/ui/general/messagebox.h"
#include "library/sp.h"
#include <iomanip>

// msvs reports level 3 warning 4996 - std::localtime is unsafe
#pragma warning(disable : 4996)

namespace OpenApoc
{
const UString existingSaveItemId = "EXISTING_SAVE_SLOT";
const UString newSaveItemId = "NEW_SAVE_SLOT";

SaveMenu::SaveMenu(SaveMenuAction saveMenuAction, sp<GameState> state)
    : Stage(), menuform(ui().GetForm("FORM_SAVEMENU")), currentState(state),
      currentAction(saveMenuAction)
{
	activeTextEdit = nullptr;
}

SaveMenu::~SaveMenu() {}

void SaveMenu::Begin()
{
	menuform->FindControlTyped<Label>("TEXT_FUNDS")->SetText(currentState->getPlayerBalance());

	auto saveListBox = menuform->FindControlTyped<ListBox>("LISTBOX_OPTIONS");

	UString titleLabelName;
	bool newItemSlotVisible = false;
	bool showAutoSaves = false;
	switch (currentAction)
	{
		case SaveMenuAction::LoadNewGame:
		case SaveMenuAction::Load:
			titleLabelName = "LABEL_LOADGAME";
			showAutoSaves = true;
			break;
		case SaveMenuAction::Save:
			titleLabelName = "LABEL_SAVEGAME";
			newItemSlotVisible = true;
			break;
		case SaveMenuAction::Delete:
			titleLabelName = "LABEL_DELETEGAME";
			break;
	}

	if (!titleLabelName.empty())
	{
		auto label = menuform->FindControlTyped<Label>(titleLabelName);
		if (label)
		{
			label->Visible = true;
		}
	}

	if (newItemSlotVisible == true)
	{
		auto newItemSlot = saveListBox->FindControl(newSaveItemId);
		if (newItemSlot != nullptr)
		{
			newItemSlot->Visible = true;
		}
	}

	// load menu items
	auto saves = saveManager.getSaveList();
	auto existingSlotControl = saveListBox->FindControl(existingSaveItemId);
	if (existingSlotControl != nullptr)
	{
		for (auto it = saves.begin(); it != saves.end(); ++it)
		{
			if (it->getType() != SaveType::Manual && !showAutoSaves)
			{ // skip quicksave and autosave on save screen
				continue;
			}

			auto newControl = existingSlotControl->CopyTo(saveListBox);
			newControl->SetData(mksp<SaveMetadata>(*it));
			auto nameLabel = newControl->FindControlTyped<Label>("LABEL_NAME");
			if (nameLabel != nullptr)
			{
				nameLabel->SetText(it->getName());
			}

			auto saveTimeLabel = newControl->FindControlTyped<Label>("LABEL_TIME");
			if (saveTimeLabel != nullptr)
			{
				std::time_t timestamp = it->getCreationDate();
				struct tm *tminfo = std::localtime(&timestamp);
				std::stringstream ss;
				if (timestamp != 0 && tminfo != nullptr)
				{
					ss << std::put_time(tminfo, "%d/%m/%y %T");
				}
				saveTimeLabel->SetText(ss.str());
			}

			GameTime gameTime(it->getGameTicks());
			auto gameDayLabel = newControl->FindControlTyped<Label>("LABEL_INGAME_DAY");
			if (gameDayLabel != nullptr)
			{
				if (it->getGameTicks() == 0 && gameTime.getDay() == 0)
				{
					gameDayLabel->SetText("");
				}
				else
				{
					gameDayLabel->SetText("Day " + std::to_string(gameTime.getDay()));
				}
			}
			auto gameTimeLabel = newControl->FindControlTyped<Label>("LABEL_INGAME_TIME");
			if (gameTimeLabel != nullptr)
			{
				if (it->getGameTicks() == 0 && gameTime.getDay() == 0)
				{
					gameTimeLabel->SetText("");
				}
				else
				{
					gameTimeLabel->SetText(gameTime.getTimeString());
				}
			}

			auto difficultyLabel = newControl->FindControlTyped<Label>("LABEL_DIFFICULTY");
			if (difficultyLabel != nullptr)
			{
				difficultyLabel->SetText(it->getDifficulty());
			}

			auto saveNameTextEdit = newControl->FindControlTyped<TextEdit>("TEXTEDIT_SAVE_NAME");
			if (currentAction != SaveMenuAction::Save && saveNameTextEdit != nullptr)
			{
				saveNameTextEdit->Visible = false;
			}
		}
		existingSlotControl->Visible = false;
	}
}

void SaveMenu::Pause() {}

void SaveMenu::Resume() {}

void SaveMenu::Finish() {}

void SaveMenu::clearTextEdit(sp<TextEdit> textEdit)
{
	auto e = OpenApoc::FormsEvent();
	e.Forms().RaisedBy = textEdit->shared_from_this();
	e.Forms().EventFlag = FormEventType::LostFocus;
	textEdit->EventOccured(&e);

	// reset text
	textEdit->Visible = false;
	auto listItem = textEdit->GetParent();
	if (listItem)
	{
		auto nameLabel = listItem->FindControlTyped<Label>("LABEL_NAME");
		if (nameLabel)
		{
			nameLabel->Visible = true;
		}
	}
	activeTextEdit = nullptr;
}

void SaveMenu::beginEditing(sp<TextEdit> textEdit, sp<TextEdit> activeTextEdit)
{
	if (activeTextEdit != nullptr)
	{
		clearTextEdit(activeTextEdit);
	}

	auto e = OpenApoc::FormsEvent();
	e.Forms().RaisedBy = textEdit->shared_from_this();
	e.Forms().EventFlag = FormEventType::MouseClick;
	textEdit->EventOccured(&e);
	textEdit->Visible = true;
	textEdit->SetAllowedCharacters(
	    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,. -_");
	textEdit->SetTextMaxSize(32);
	auto listItem = textEdit->GetParent();
	if (listItem)
	{
		auto nameLabel = listItem->FindControlTyped<Label>("LABEL_NAME");
		if (nameLabel)
		{
			nameLabel->Visible = false;
			if (nameLabel->GetParent() && nameLabel->GetParent()->Name != "NEW_SAVE_SLOT")
			{
				textEdit->SetText(nameLabel->GetText());
			}
			else
			{
				textEdit->SetText("");
			}
		}
	}
}

void SaveMenu::loadWithWarning(sp<Control> parent)
{
	if (parent->Name == existingSaveItemId)
	{
		sp<SaveMetadata> slot = parent->GetData<SaveMetadata>();
		if (slot != nullptr)
		{
			std::function<void()> onSuccess = std::function<void()>([this, slot] {
				stageCmd.nextStage = mksp<LoadingScreen>(std::move(saveManager.loadGame(*slot)));
				stageCmd.cmd = StageCmd::Command::PUSH;
			});
			sp<MessageBox> messageBox = mksp<MessageBox>(
			    MessageBox("Load game", "Unsaved progress will be lost. Continue?",
			               MessageBox::ButtonOptions::YesNo, std::move(onSuccess), nullptr));

			stageCmd.nextStage = messageBox;
			stageCmd.cmd = StageCmd::Command::PUSH;
		}
	}
}

void SaveMenu::tryToLoadGame(sp<Control> slotControl)
{
	if (slotControl->Name == existingSaveItemId)
	{
		sp<SaveMetadata> slot = slotControl->GetData<SaveMetadata>();
		if (slot != nullptr)
		{
			stageCmd.nextStage = mksp<LoadingScreen>(std::move(saveManager.loadGame(*slot)));
			stageCmd.cmd = StageCmd::Command::PUSH;
		}
	}
}

void SaveMenu::tryToSaveGame(const UString &saveName, sp<Control> parent)
{
	if (parent->Name == newSaveItemId)
	{
		if (saveManager.newSaveGame(saveName, currentState))
		{
			stageCmd.cmd = StageCmd::Command::POP;
		}
		else
		{
			clearTextEdit(activeTextEdit);
		}
	}
	else
	{
		sp<SaveMetadata> slot = parent->GetData<SaveMetadata>();
		std::function<void()> onSuccess = std::function<void()>([this, slot, saveName] {
			if (saveManager.overrideGame(*slot, saveName, currentState))
			{
				stageCmd.cmd = StageCmd::Command::POP;
			}
			else
			{
				clearTextEdit(activeTextEdit);
			}
		});
		std::function<void()> onCancel =
		    std::function<void()>([this] { clearTextEdit(activeTextEdit); });
		sp<MessageBox> messageBox = mksp<MessageBox>(MessageBox(
		    "Override saved game", "Do you really want to override " + slot->getName() + "?",
		    MessageBox::ButtonOptions::YesNo, std::move(onSuccess), std::move(onCancel)));

		stageCmd.nextStage = messageBox;
		stageCmd.cmd = StageCmd::Command::PUSH;
	}
}

void SaveMenu::tryToDeleteSavedGame(sp<Control> &slotControl)
{
	sp<SaveMetadata> slot = slotControl->GetData<SaveMetadata>();
	std::function<void()> onSuccess = std::function<void()>([this, slotControl, slot] {
		if (saveManager.deleteGame(slot))
		{
			// no way to pop
			slotControl->Visible = false;
		}
	});
	sp<MessageBox> messageBox = mksp<MessageBox>(
	    MessageBox("Delete saved game", "Do you really want to delete " + slot->getName() + "?",
	               MessageBox::ButtonOptions::YesNo, std::move(onSuccess), nullptr));

	stageCmd.nextStage = messageBox;
	stageCmd.cmd = StageCmd::Command::PUSH;
}

void SaveMenu::EventOccurred(Event *e)
{
	menuform->EventOccured(e);

	if (e->Type() == EVENT_KEY_DOWN)
	{
		if (e->Keyboard().KeyCode == SDLK_ESCAPE)
		{
			stageCmd.cmd = StageCmd::Command::POP;
			return;
		}
	}

	if (e->Type() == EVENT_FORM_INTERACTION)
	{
		switch (e->Forms().EventFlag)
		{
			case FormEventType::ButtonClick:
				if (e->Forms().RaisedBy->Name == "BUTTON_QUIT")
				{
					stageCmd.cmd = StageCmd::Command::POP;
				}
				else
				{
					sp<Control> slotControl;
					switch (currentAction)
					{
						case SaveMenuAction::LoadNewGame:
							slotControl = e->Forms().RaisedBy->GetParent();
							if (slotControl)
							{
								tryToLoadGame(slotControl);
							}
							break;
						case SaveMenuAction::Load:
							slotControl = e->Forms().RaisedBy->GetParent();
							if (slotControl)
							{
								loadWithWarning(slotControl);
							}
							break;
						case SaveMenuAction::Save:
							slotControl = e->Forms().RaisedBy->GetParent();
							if (slotControl)
							{
								auto nameEdit =
								    slotControl->FindControlTyped<TextEdit>("TEXTEDIT_SAVE_NAME");
								if (nameEdit && nameEdit != activeTextEdit)
								{
									beginEditing(nameEdit, activeTextEdit);
									activeTextEdit = nameEdit;
								}
							}
							break;
						case SaveMenuAction::Delete:
							slotControl = e->Forms().RaisedBy->GetParent();
							if (slotControl)
							{
								tryToDeleteSavedGame(slotControl);
							}
							break;
					}
				}
				break;
			case FormEventType::TextEditFinish:
				sp<TextEdit> textEdit = std::static_pointer_cast<TextEdit>(e->Forms().RaisedBy);
				auto slotControl = e->Forms().RaisedBy->GetParent();
				if (!slotControl || !textEdit || (textEdit != activeTextEdit))
				{
					return;
				}

				tryToSaveGame(textEdit->GetText(), slotControl);
				break;
		}
	}
}

void SaveMenu::Update(StageCmd *const cmd)
{
	menuform->Update();
	*cmd = this->stageCmd;
	// Reset the command to default
	this->stageCmd = StageCmd();
}

void SaveMenu::Render()
{
	fw().Stage_GetPrevious(this->shared_from_this())->Render();
	fw().renderer->drawFilledRect(Vec2<float>(0, 0),
	                              Vec2<float>(fw().Display_GetWidth(), fw().Display_GetHeight()),
	                              Colour(0, 0, 0, 128));
	menuform->Render();
}

bool SaveMenu::IsTransition() { return false; }
}; // namespace OpenApoc
<commit_msg>Fix savemenu crash<commit_after>#include "game/ui/general/savemenu.h"
#include "forms/ui.h"
#include "framework/event.h"
#include "framework/framework.h"
#include "game/state/gamestate.h"
#include "game/ui/general/loadingscreen.h"
#include "game/ui/general/messagebox.h"
#include "library/sp.h"
#include <iomanip>

// msvs reports level 3 warning 4996 - std::localtime is unsafe
#pragma warning(disable : 4996)

namespace OpenApoc
{
const UString existingSaveItemId = "EXISTING_SAVE_SLOT";
const UString newSaveItemId = "NEW_SAVE_SLOT";

SaveMenu::SaveMenu(SaveMenuAction saveMenuAction, sp<GameState> state)
    : Stage(), menuform(ui().GetForm("FORM_SAVEMENU")), currentState(state),
      currentAction(saveMenuAction)
{
	activeTextEdit = nullptr;
}

SaveMenu::~SaveMenu() {}

void SaveMenu::Begin()
{
	if (currentState)
	{
		menuform->FindControlTyped<Label>("TEXT_FUNDS")->SetText(currentState->getPlayerBalance());
	}

	auto saveListBox = menuform->FindControlTyped<ListBox>("LISTBOX_OPTIONS");

	UString titleLabelName;
	bool newItemSlotVisible = false;
	bool showAutoSaves = false;
	switch (currentAction)
	{
		case SaveMenuAction::LoadNewGame:
		case SaveMenuAction::Load:
			titleLabelName = "LABEL_LOADGAME";
			showAutoSaves = true;
			break;
		case SaveMenuAction::Save:
			titleLabelName = "LABEL_SAVEGAME";
			newItemSlotVisible = true;
			break;
		case SaveMenuAction::Delete:
			titleLabelName = "LABEL_DELETEGAME";
			break;
	}

	if (!titleLabelName.empty())
	{
		auto label = menuform->FindControlTyped<Label>(titleLabelName);
		if (label)
		{
			label->Visible = true;
		}
	}

	if (newItemSlotVisible == true)
	{
		auto newItemSlot = saveListBox->FindControl(newSaveItemId);
		if (newItemSlot != nullptr)
		{
			newItemSlot->Visible = true;
		}
	}

	// load menu items
	auto saves = saveManager.getSaveList();
	auto existingSlotControl = saveListBox->FindControl(existingSaveItemId);
	if (existingSlotControl != nullptr)
	{
		for (auto it = saves.begin(); it != saves.end(); ++it)
		{
			if (it->getType() != SaveType::Manual && !showAutoSaves)
			{ // skip quicksave and autosave on save screen
				continue;
			}

			auto newControl = existingSlotControl->CopyTo(saveListBox);
			newControl->SetData(mksp<SaveMetadata>(*it));
			auto nameLabel = newControl->FindControlTyped<Label>("LABEL_NAME");
			if (nameLabel != nullptr)
			{
				nameLabel->SetText(it->getName());
			}

			auto saveTimeLabel = newControl->FindControlTyped<Label>("LABEL_TIME");
			if (saveTimeLabel != nullptr)
			{
				std::time_t timestamp = it->getCreationDate();
				struct tm *tminfo = std::localtime(&timestamp);
				std::stringstream ss;
				if (timestamp != 0 && tminfo != nullptr)
				{
					ss << std::put_time(tminfo, "%d/%m/%y %T");
				}
				saveTimeLabel->SetText(ss.str());
			}

			GameTime gameTime(it->getGameTicks());
			auto gameDayLabel = newControl->FindControlTyped<Label>("LABEL_INGAME_DAY");
			if (gameDayLabel != nullptr)
			{
				if (it->getGameTicks() == 0 && gameTime.getDay() == 0)
				{
					gameDayLabel->SetText("");
				}
				else
				{
					gameDayLabel->SetText("Day " + std::to_string(gameTime.getDay()));
				}
			}
			auto gameTimeLabel = newControl->FindControlTyped<Label>("LABEL_INGAME_TIME");
			if (gameTimeLabel != nullptr)
			{
				if (it->getGameTicks() == 0 && gameTime.getDay() == 0)
				{
					gameTimeLabel->SetText("");
				}
				else
				{
					gameTimeLabel->SetText(gameTime.getTimeString());
				}
			}

			auto difficultyLabel = newControl->FindControlTyped<Label>("LABEL_DIFFICULTY");
			if (difficultyLabel != nullptr)
			{
				difficultyLabel->SetText(it->getDifficulty());
			}

			auto saveNameTextEdit = newControl->FindControlTyped<TextEdit>("TEXTEDIT_SAVE_NAME");
			if (currentAction != SaveMenuAction::Save && saveNameTextEdit != nullptr)
			{
				saveNameTextEdit->Visible = false;
			}
		}
		existingSlotControl->Visible = false;
	}
}

void SaveMenu::Pause() {}

void SaveMenu::Resume() {}

void SaveMenu::Finish() {}

void SaveMenu::clearTextEdit(sp<TextEdit> textEdit)
{
	auto e = OpenApoc::FormsEvent();
	e.Forms().RaisedBy = textEdit->shared_from_this();
	e.Forms().EventFlag = FormEventType::LostFocus;
	textEdit->EventOccured(&e);

	// reset text
	textEdit->Visible = false;
	auto listItem = textEdit->GetParent();
	if (listItem)
	{
		auto nameLabel = listItem->FindControlTyped<Label>("LABEL_NAME");
		if (nameLabel)
		{
			nameLabel->Visible = true;
		}
	}
	activeTextEdit = nullptr;
}

void SaveMenu::beginEditing(sp<TextEdit> textEdit, sp<TextEdit> activeTextEdit)
{
	if (activeTextEdit != nullptr)
	{
		clearTextEdit(activeTextEdit);
	}

	auto e = OpenApoc::FormsEvent();
	e.Forms().RaisedBy = textEdit->shared_from_this();
	e.Forms().EventFlag = FormEventType::MouseClick;
	textEdit->EventOccured(&e);
	textEdit->Visible = true;
	textEdit->SetAllowedCharacters(
	    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,. -_");
	textEdit->SetTextMaxSize(32);
	auto listItem = textEdit->GetParent();
	if (listItem)
	{
		auto nameLabel = listItem->FindControlTyped<Label>("LABEL_NAME");
		if (nameLabel)
		{
			nameLabel->Visible = false;
			if (nameLabel->GetParent() && nameLabel->GetParent()->Name != "NEW_SAVE_SLOT")
			{
				textEdit->SetText(nameLabel->GetText());
			}
			else
			{
				textEdit->SetText("");
			}
		}
	}
}

void SaveMenu::loadWithWarning(sp<Control> parent)
{
	if (parent->Name == existingSaveItemId)
	{
		sp<SaveMetadata> slot = parent->GetData<SaveMetadata>();
		if (slot != nullptr)
		{
			std::function<void()> onSuccess = std::function<void()>([this, slot] {
				stageCmd.nextStage = mksp<LoadingScreen>(std::move(saveManager.loadGame(*slot)));
				stageCmd.cmd = StageCmd::Command::PUSH;
			});
			sp<MessageBox> messageBox = mksp<MessageBox>(
			    MessageBox("Load game", "Unsaved progress will be lost. Continue?",
			               MessageBox::ButtonOptions::YesNo, std::move(onSuccess), nullptr));

			stageCmd.nextStage = messageBox;
			stageCmd.cmd = StageCmd::Command::PUSH;
		}
	}
}

void SaveMenu::tryToLoadGame(sp<Control> slotControl)
{
	if (slotControl->Name == existingSaveItemId)
	{
		sp<SaveMetadata> slot = slotControl->GetData<SaveMetadata>();
		if (slot != nullptr)
		{
			stageCmd.nextStage = mksp<LoadingScreen>(std::move(saveManager.loadGame(*slot)));
			stageCmd.cmd = StageCmd::Command::PUSH;
		}
	}
}

void SaveMenu::tryToSaveGame(const UString &saveName, sp<Control> parent)
{
	if (parent->Name == newSaveItemId)
	{
		if (saveManager.newSaveGame(saveName, currentState))
		{
			stageCmd.cmd = StageCmd::Command::POP;
		}
		else
		{
			clearTextEdit(activeTextEdit);
		}
	}
	else
	{
		sp<SaveMetadata> slot = parent->GetData<SaveMetadata>();
		std::function<void()> onSuccess = std::function<void()>([this, slot, saveName] {
			if (saveManager.overrideGame(*slot, saveName, currentState))
			{
				stageCmd.cmd = StageCmd::Command::POP;
			}
			else
			{
				clearTextEdit(activeTextEdit);
			}
		});
		std::function<void()> onCancel =
		    std::function<void()>([this] { clearTextEdit(activeTextEdit); });
		sp<MessageBox> messageBox = mksp<MessageBox>(MessageBox(
		    "Override saved game", "Do you really want to override " + slot->getName() + "?",
		    MessageBox::ButtonOptions::YesNo, std::move(onSuccess), std::move(onCancel)));

		stageCmd.nextStage = messageBox;
		stageCmd.cmd = StageCmd::Command::PUSH;
	}
}

void SaveMenu::tryToDeleteSavedGame(sp<Control> &slotControl)
{
	sp<SaveMetadata> slot = slotControl->GetData<SaveMetadata>();
	std::function<void()> onSuccess = std::function<void()>([this, slotControl, slot] {
		if (saveManager.deleteGame(slot))
		{
			// no way to pop
			slotControl->Visible = false;
		}
	});
	sp<MessageBox> messageBox = mksp<MessageBox>(
	    MessageBox("Delete saved game", "Do you really want to delete " + slot->getName() + "?",
	               MessageBox::ButtonOptions::YesNo, std::move(onSuccess), nullptr));

	stageCmd.nextStage = messageBox;
	stageCmd.cmd = StageCmd::Command::PUSH;
}

void SaveMenu::EventOccurred(Event *e)
{
	menuform->EventOccured(e);

	if (e->Type() == EVENT_KEY_DOWN)
	{
		if (e->Keyboard().KeyCode == SDLK_ESCAPE)
		{
			stageCmd.cmd = StageCmd::Command::POP;
			return;
		}
	}

	if (e->Type() == EVENT_FORM_INTERACTION)
	{
		switch (e->Forms().EventFlag)
		{
			case FormEventType::ButtonClick:
				if (e->Forms().RaisedBy->Name == "BUTTON_QUIT")
				{
					stageCmd.cmd = StageCmd::Command::POP;
				}
				else
				{
					sp<Control> slotControl;
					switch (currentAction)
					{
						case SaveMenuAction::LoadNewGame:
							slotControl = e->Forms().RaisedBy->GetParent();
							if (slotControl)
							{
								tryToLoadGame(slotControl);
							}
							break;
						case SaveMenuAction::Load:
							slotControl = e->Forms().RaisedBy->GetParent();
							if (slotControl)
							{
								loadWithWarning(slotControl);
							}
							break;
						case SaveMenuAction::Save:
							slotControl = e->Forms().RaisedBy->GetParent();
							if (slotControl)
							{
								auto nameEdit =
								    slotControl->FindControlTyped<TextEdit>("TEXTEDIT_SAVE_NAME");
								if (nameEdit && nameEdit != activeTextEdit)
								{
									beginEditing(nameEdit, activeTextEdit);
									activeTextEdit = nameEdit;
								}
							}
							break;
						case SaveMenuAction::Delete:
							slotControl = e->Forms().RaisedBy->GetParent();
							if (slotControl)
							{
								tryToDeleteSavedGame(slotControl);
							}
							break;
					}
				}
				break;
			case FormEventType::TextEditFinish:
				sp<TextEdit> textEdit = std::static_pointer_cast<TextEdit>(e->Forms().RaisedBy);
				auto slotControl = e->Forms().RaisedBy->GetParent();
				if (!slotControl || !textEdit || (textEdit != activeTextEdit))
				{
					return;
				}

				tryToSaveGame(textEdit->GetText(), slotControl);
				break;
		}
	}
}

void SaveMenu::Update(StageCmd *const cmd)
{
	menuform->Update();
	*cmd = this->stageCmd;
	// Reset the command to default
	this->stageCmd = StageCmd();
}

void SaveMenu::Render()
{
	fw().Stage_GetPrevious(this->shared_from_this())->Render();
	fw().renderer->drawFilledRect(Vec2<float>(0, 0),
	                              Vec2<float>(fw().Display_GetWidth(), fw().Display_GetHeight()),
	                              Colour(0, 0, 0, 128));
	menuform->Render();
}

bool SaveMenu::IsTransition() { return false; }
}; // namespace OpenApoc
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "quicktest.h"
#include "quicktestresult_p.h"
#include <QtTest/qtestsystem.h>
#include "qtestoptions_p.h"
#include <QApplication>
#include <QtDeclarative/qdeclarative.h>
#include <QtQuick1/qdeclarativeview.h>
#include <QtDeclarative/qdeclarativeengine.h>
#include <QtDeclarative/qdeclarativecontext.h>
#if defined(QML_VERSION) && QML_VERSION >= 0x020000
#include <QtQuick/qquickview.h>
#define QUICK_TEST_SCENEGRAPH 1
#endif
#include <QtDeclarative/qjsvalue.h>
#include <QtDeclarative/qjsengine.h>
#include <QtGui/qopengl.h>
#include <QtCore/qurl.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qfile.h>
#include <QtCore/qdebug.h>
#include <QtCore/qeventloop.h>
#include <QtGui/qtextdocument.h>
#include <stdio.h>
#include <QtGui/QGuiApplication>
#include <QtCore/QTranslator>
QT_BEGIN_NAMESPACE

class QTestRootObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool windowShown READ windowShown NOTIFY windowShownChanged)
    Q_PROPERTY(bool hasTestCase READ hasTestCase WRITE setHasTestCase NOTIFY hasTestCaseChanged)
public:
    QTestRootObject(QObject *parent = 0)
        : QObject(parent), hasQuit(false), m_windowShown(false), m_hasTestCase(false)  {}

    bool hasQuit:1;
    bool hasTestCase() const { return m_hasTestCase; }
    void setHasTestCase(bool value) { m_hasTestCase = value; emit hasTestCaseChanged(); }

    bool windowShown() const { return m_windowShown; }
    void setWindowShown(bool value) { m_windowShown = value; emit windowShownChanged(); }

Q_SIGNALS:
    void windowShownChanged();
    void hasTestCaseChanged();

private Q_SLOTS:
    void quit() { hasQuit = true; }

private:
    bool m_windowShown : 1;
    bool m_hasTestCase :1;
};

static inline QString stripQuotes(const QString &s)
{
    if (s.length() >= 2 && s.startsWith(QLatin1Char('"')) && s.endsWith(QLatin1Char('"')))
        return s.mid(1, s.length() - 2);
    else
        return s;
}

int quick_test_main(int argc, char **argv, const char *name, quick_test_viewport_create createViewport, const char *sourceDir)
{
    QGuiApplication* app = 0;
    if (!QCoreApplication::instance()) {
        app = new QGuiApplication(argc, argv);
    }

    // Look for QML-specific command-line options.
    //      -import dir         Specify an import directory.
    //      -input dir          Specify the input directory for test cases.
    //      -qtquick1           Run with QtQuick 1 rather than QtQuick 2.
    //      -translation file   Specify the translation file.
    QStringList imports;
    QString testPath;
    QString translationFile;
    bool qtQuick2 = true;
    int outargc = 1;
    int index = 1;
    while (index < argc) {
        if (strcmp(argv[index], "-import") == 0 && (index + 1) < argc) {
            imports += stripQuotes(QString::fromLocal8Bit(argv[index + 1]));
            index += 2;
        } else if (strcmp(argv[index], "-input") == 0 && (index + 1) < argc) {
            testPath = stripQuotes(QString::fromLocal8Bit(argv[index + 1]));
            index += 2;
        } else if (strcmp(argv[index], "-opengl") == 0) {
            ++index;
        } else if (strcmp(argv[index], "-qtquick1") == 0) {
            qtQuick2 = false;
            ++index;
        } else if (strcmp(argv[index], "-translation") == 0 && (index + 1) < argc) {
            translationFile = stripQuotes(QString::fromLocal8Bit(argv[index + 1]));
            index += 2;
        } else if (outargc != index) {
            argv[outargc++] = argv[index++];
        } else {
            ++outargc;
            ++index;
        }
    }
    argv[outargc] = 0;
    argc = outargc;

    // Parse the command-line arguments.
    QuickTestResult::parseArgs(argc, argv);
    QuickTestResult::setProgramName(name);

    QTranslator translator;
    if (!translationFile.isEmpty()) {
        if (translator.load(translationFile)) {
            app->installTranslator(&translator);
        } else {
            qWarning() << "Could not load the translation file" << translationFile;
        }
    }

    // Determine where to look for the test data.
    if (testPath.isEmpty() && sourceDir)
        testPath = QString::fromLocal8Bit(sourceDir);
    if (testPath.isEmpty())
        testPath = QLatin1String(".");

    QStringList files;

    if (testPath.endsWith(QLatin1String(".qml")) && QFileInfo(testPath).isFile()) {
        files << testPath;
    } else {
        // Scan the test data directory recursively, looking for "tst_*.qml" files.
        QStringList filters;
        filters += QLatin1String("tst_*.qml");
        QDirIterator iter(testPath, filters, QDir::Files,
                          QDirIterator::Subdirectories |
                          QDirIterator::FollowSymlinks);
        while (iter.hasNext())
            files += iter.next();
        files.sort();
    }

    // Bail out if we didn't find any test cases.
    if (files.isEmpty()) {
        qWarning() << argv[0] << ": could not find any test cases under"
                   << testPath;
        return 1;
    }

    // Scan through all of the "tst_*.qml" files and run each of them
    // in turn with a QDeclarativeView.
#ifdef QUICK_TEST_SCENEGRAPH
    if (qtQuick2) {
        QQuickView view;
        QTestRootObject rootobj;
        QEventLoop eventLoop;
        QObject::connect(view.engine(), SIGNAL(quit()),
                         &rootobj, SLOT(quit()));
        QObject::connect(view.engine(), SIGNAL(quit()),
                         &eventLoop, SLOT(quit()));
        view.rootContext()->setContextProperty
            (QLatin1String("qtest"), &rootobj);
        foreach (QString path, imports)
            view.engine()->addImportPath(path);

        foreach (QString file, files) {
            QFileInfo fi(file);
            if (!fi.exists())
                continue;

            rootobj.setHasTestCase(false);
            rootobj.setWindowShown(false);
            rootobj.hasQuit = false;
            QString path = fi.absoluteFilePath();
            if (path.startsWith(QLatin1String(":/")))
                view.setSource(QUrl(QLatin1String("qrc:") + path.mid(2)));
            else
                view.setSource(QUrl::fromLocalFile(path));

            if (QTest::printAvailableFunctions)
                continue;
            if (view.status() == QQuickView::Error) {
                // Error compiling the test - flag failure in the log and continue.
                QList<QDeclarativeError> errors = view.errors();
                QuickTestResult results;
                results.setTestCaseName(fi.baseName());
                results.startLogging();
                results.setFunctionName(QLatin1String("compile"));
                results.setFunctionType(QuickTestResult::Func);
                results.fail(errors.at(0).description(),
                             errors.at(0).url().toString(),
                             errors.at(0).line());
                results.finishTestFunction();
                results.setFunctionName(QString());
                results.setFunctionType(QuickTestResult::NoWhere);
                results.stopLogging();
                continue;
            }
            if (!rootobj.hasQuit) {
                // If the test already quit, then it was performed
                // synchronously during setSource().  Otherwise it is
                // an asynchronous test and we need to show the window
                // and wait for the quit indication.
                view.show();
                QTest::qWaitForWindowShown(&view);
                rootobj.setWindowShown(true);
                if (!rootobj.hasQuit && rootobj.hasTestCase())
                    eventLoop.exec();
            }
        }
    } else
#endif
    {
        foreach (QString file, files) {
            QFileInfo fi(file);
            if (!fi.exists())
                continue;
            QDeclarativeView view;
            QTestRootObject rootobj;
            QEventLoop eventLoop;
            QObject::connect(view.engine(), SIGNAL(quit()),
                             &rootobj, SLOT(quit()));
            QObject::connect(view.engine(), SIGNAL(quit()),
                             &eventLoop, SLOT(quit()));
            if (createViewport)
                view.setViewport((*createViewport)());
            view.rootContext()->setContextProperty
                (QLatin1String("qtest"), &rootobj);
            foreach (QString path, imports)
                view.engine()->addImportPath(path);
            QString path = fi.absoluteFilePath();
            if (path.startsWith(QLatin1String(":/")))
                view.setSource(QUrl(QLatin1String("qrc:") + path.mid(2)));
            else
                view.setSource(QUrl::fromLocalFile(path));
            if (QTest::printAvailableFunctions)
                continue;
            if (view.status() == QDeclarativeView::Error) {
                // Error compiling the test - flag failure in the log and continue.
                QList<QDeclarativeError> errors = view.errors();
                QuickTestResult results;
                results.setTestCaseName(fi.baseName());
                results.startLogging();
                results.setFunctionName(QLatin1String("compile"));
                results.setFunctionType(QuickTestResult::Func);
                results.fail(errors.at(0).description(),
                             errors.at(0).url().toString(),
                             errors.at(0).line());
                results.finishTestFunction();
                results.setFunctionName(QString());
                results.setFunctionType(QuickTestResult::NoWhere);
                results.stopLogging();
                continue;
            }
            if (!rootobj.hasQuit) {
                // If the test already quit, then it was performed
                // synchronously during setSource().  Otherwise it is
                // an asynchronous test and we need to show the window
                // and wait for the quit indication.
                view.show();
                QTest::qWaitForWindowShown(&view);
                rootobj.setWindowShown(true);
                if (!rootobj.hasQuit)
                    eventLoop.exec();
            }
        }
    }

    // Flush the current logging stream.
    QuickTestResult::setProgramName(0);

    delete app;
    // Return the number of failures as the exit code.
    return QuickTestResult::exitCode();
}

QT_END_NAMESPACE

#include "quicktest.moc"
<commit_msg>Integrate testcocoon support into QtQuickTest.<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "quicktest.h"
#include "quicktestresult_p.h"
#include <QtTest/qtestsystem.h>
#include "qtestoptions_p.h"
#include <QApplication>
#include <QtDeclarative/qdeclarative.h>
#include <QtQuick1/qdeclarativeview.h>
#include <QtDeclarative/qdeclarativeengine.h>
#include <QtDeclarative/qdeclarativecontext.h>
#if defined(QML_VERSION) && QML_VERSION >= 0x020000
#include <QtQuick/qquickview.h>
#define QUICK_TEST_SCENEGRAPH 1
#endif
#include <QtDeclarative/qjsvalue.h>
#include <QtDeclarative/qjsengine.h>
#include <QtGui/qopengl.h>
#include <QtCore/qurl.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qfile.h>
#include <QtCore/qdebug.h>
#include <QtCore/qeventloop.h>
#include <QtGui/qtextdocument.h>
#include <stdio.h>
#include <QtGui/QGuiApplication>
#include <QtCore/QTranslator>
QT_BEGIN_NAMESPACE

static void installCoverageTool(const char * appname, const char * testname)
{
#ifdef __COVERAGESCANNER__
    // Install Coverage Tool
    __coveragescanner_install(appname);
    __coveragescanner_testname(testname);
    __coveragescanner_clear();
#else
    Q_UNUSED(appname);
    Q_UNUSED(testname);
#endif
}

static void saveCoverageTool(const char * appname, bool testfailed)
{
#ifdef __COVERAGESCANNER__
    // install again to make sure the filename is correct.
    // without this, a plugin or similar may have changed the filename.
    __coveragescanner_install(appname);
    __coveragescanner_teststate(testfailed ? "FAILED" : "PASSED");
    __coveragescanner_save();
    __coveragescanner_testname("");
    __coveragescanner_clear();
#else
    Q_UNUSED(appname);
    Q_UNUSED(testfailed);
#endif
}

class QTestRootObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool windowShown READ windowShown NOTIFY windowShownChanged)
    Q_PROPERTY(bool hasTestCase READ hasTestCase WRITE setHasTestCase NOTIFY hasTestCaseChanged)
public:
    QTestRootObject(QObject *parent = 0)
        : QObject(parent), hasQuit(false), m_windowShown(false), m_hasTestCase(false)  {}

    bool hasQuit:1;
    bool hasTestCase() const { return m_hasTestCase; }
    void setHasTestCase(bool value) { m_hasTestCase = value; emit hasTestCaseChanged(); }

    bool windowShown() const { return m_windowShown; }
    void setWindowShown(bool value) { m_windowShown = value; emit windowShownChanged(); }

Q_SIGNALS:
    void windowShownChanged();
    void hasTestCaseChanged();

private Q_SLOTS:
    void quit() { hasQuit = true; }

private:
    bool m_windowShown : 1;
    bool m_hasTestCase :1;
};

static inline QString stripQuotes(const QString &s)
{
    if (s.length() >= 2 && s.startsWith(QLatin1Char('"')) && s.endsWith(QLatin1Char('"')))
        return s.mid(1, s.length() - 2);
    else
        return s;
}

int quick_test_main(int argc, char **argv, const char *name, quick_test_viewport_create createViewport, const char *sourceDir)
{
    QGuiApplication* app = 0;
    if (!QCoreApplication::instance()) {
        app = new QGuiApplication(argc, argv);
    }

    // Look for QML-specific command-line options.
    //      -import dir         Specify an import directory.
    //      -input dir          Specify the input directory for test cases.
    //      -qtquick1           Run with QtQuick 1 rather than QtQuick 2.
    //      -translation file   Specify the translation file.
    QStringList imports;
    QString testPath;
    QString translationFile;
    bool qtQuick2 = true;
    int outargc = 1;
    int index = 1;
    while (index < argc) {
        if (strcmp(argv[index], "-import") == 0 && (index + 1) < argc) {
            imports += stripQuotes(QString::fromLocal8Bit(argv[index + 1]));
            index += 2;
        } else if (strcmp(argv[index], "-input") == 0 && (index + 1) < argc) {
            testPath = stripQuotes(QString::fromLocal8Bit(argv[index + 1]));
            index += 2;
        } else if (strcmp(argv[index], "-opengl") == 0) {
            ++index;
        } else if (strcmp(argv[index], "-qtquick1") == 0) {
            qtQuick2 = false;
            ++index;
        } else if (strcmp(argv[index], "-translation") == 0 && (index + 1) < argc) {
            translationFile = stripQuotes(QString::fromLocal8Bit(argv[index + 1]));
            index += 2;
        } else if (outargc != index) {
            argv[outargc++] = argv[index++];
        } else {
            ++outargc;
            ++index;
        }
    }
    argv[outargc] = 0;
    argc = outargc;

    // Parse the command-line arguments.
    QuickTestResult::parseArgs(argc, argv);
    QuickTestResult::setProgramName(name);

    installCoverageTool(argv[0], name);

    QTranslator translator;
    if (!translationFile.isEmpty()) {
        if (translator.load(translationFile)) {
            app->installTranslator(&translator);
        } else {
            qWarning() << "Could not load the translation file" << translationFile;
        }
    }

    // Determine where to look for the test data.
    if (testPath.isEmpty() && sourceDir)
        testPath = QString::fromLocal8Bit(sourceDir);
    if (testPath.isEmpty())
        testPath = QLatin1String(".");

    QStringList files;

    if (testPath.endsWith(QLatin1String(".qml")) && QFileInfo(testPath).isFile()) {
        files << testPath;
    } else {
        // Scan the test data directory recursively, looking for "tst_*.qml" files.
        QStringList filters;
        filters += QLatin1String("tst_*.qml");
        QDirIterator iter(testPath, filters, QDir::Files,
                          QDirIterator::Subdirectories |
                          QDirIterator::FollowSymlinks);
        while (iter.hasNext())
            files += iter.next();
        files.sort();
    }

    // Bail out if we didn't find any test cases.
    if (files.isEmpty()) {
        qWarning() << argv[0] << ": could not find any test cases under"
                   << testPath;
        return 1;
    }

    // Scan through all of the "tst_*.qml" files and run each of them
    // in turn with a QDeclarativeView.
#ifdef QUICK_TEST_SCENEGRAPH
    if (qtQuick2) {
        QQuickView view;
        QTestRootObject rootobj;
        QEventLoop eventLoop;
        QObject::connect(view.engine(), SIGNAL(quit()),
                         &rootobj, SLOT(quit()));
        QObject::connect(view.engine(), SIGNAL(quit()),
                         &eventLoop, SLOT(quit()));
        view.rootContext()->setContextProperty
            (QLatin1String("qtest"), &rootobj);
        foreach (QString path, imports)
            view.engine()->addImportPath(path);

        foreach (QString file, files) {
            QFileInfo fi(file);
            if (!fi.exists())
                continue;

            rootobj.setHasTestCase(false);
            rootobj.setWindowShown(false);
            rootobj.hasQuit = false;
            QString path = fi.absoluteFilePath();
            if (path.startsWith(QLatin1String(":/")))
                view.setSource(QUrl(QLatin1String("qrc:") + path.mid(2)));
            else
                view.setSource(QUrl::fromLocalFile(path));

            if (QTest::printAvailableFunctions)
                continue;
            if (view.status() == QQuickView::Error) {
                // Error compiling the test - flag failure in the log and continue.
                QList<QDeclarativeError> errors = view.errors();
                QuickTestResult results;
                results.setTestCaseName(fi.baseName());
                results.startLogging();
                results.setFunctionName(QLatin1String("compile"));
                results.setFunctionType(QuickTestResult::Func);
                results.fail(errors.at(0).description(),
                             errors.at(0).url().toString(),
                             errors.at(0).line());
                results.finishTestFunction();
                results.setFunctionName(QString());
                results.setFunctionType(QuickTestResult::NoWhere);
                results.stopLogging();
                continue;
            }
            if (!rootobj.hasQuit) {
                // If the test already quit, then it was performed
                // synchronously during setSource().  Otherwise it is
                // an asynchronous test and we need to show the window
                // and wait for the quit indication.
                view.show();
                QTest::qWaitForWindowShown(&view);
                rootobj.setWindowShown(true);
                if (!rootobj.hasQuit && rootobj.hasTestCase())
                    eventLoop.exec();
            }
        }
    } else
#endif
    {
        foreach (QString file, files) {
            QFileInfo fi(file);
            if (!fi.exists())
                continue;
            QDeclarativeView view;
            QTestRootObject rootobj;
            QEventLoop eventLoop;
            QObject::connect(view.engine(), SIGNAL(quit()),
                             &rootobj, SLOT(quit()));
            QObject::connect(view.engine(), SIGNAL(quit()),
                             &eventLoop, SLOT(quit()));
            if (createViewport)
                view.setViewport((*createViewport)());
            view.rootContext()->setContextProperty
                (QLatin1String("qtest"), &rootobj);
            foreach (QString path, imports)
                view.engine()->addImportPath(path);
            QString path = fi.absoluteFilePath();
            if (path.startsWith(QLatin1String(":/")))
                view.setSource(QUrl(QLatin1String("qrc:") + path.mid(2)));
            else
                view.setSource(QUrl::fromLocalFile(path));
            if (QTest::printAvailableFunctions)
                continue;
            if (view.status() == QDeclarativeView::Error) {
                // Error compiling the test - flag failure in the log and continue.
                QList<QDeclarativeError> errors = view.errors();
                QuickTestResult results;
                results.setTestCaseName(fi.baseName());
                results.startLogging();
                results.setFunctionName(QLatin1String("compile"));
                results.setFunctionType(QuickTestResult::Func);
                results.fail(errors.at(0).description(),
                             errors.at(0).url().toString(),
                             errors.at(0).line());
                results.finishTestFunction();
                results.setFunctionName(QString());
                results.setFunctionType(QuickTestResult::NoWhere);
                results.stopLogging();
                continue;
            }
            if (!rootobj.hasQuit) {
                // If the test already quit, then it was performed
                // synchronously during setSource().  Otherwise it is
                // an asynchronous test and we need to show the window
                // and wait for the quit indication.
                view.show();
                QTest::qWaitForWindowShown(&view);
                rootobj.setWindowShown(true);
                if (!rootobj.hasQuit)
                    eventLoop.exec();
            }
        }
    }

    // Flush the current logging stream.
    QuickTestResult::setProgramName(0);

    saveCoverageTool(argv[0], QuickTestResult::exitCode());

    delete app;
    // Return the number of failures as the exit code.
    return QuickTestResult::exitCode();
}

QT_END_NAMESPACE

#include "quicktest.moc"
<|endoftext|>


<commit_before>/*
 * File:   Renderer.cpp
 * Author: morgan
 *
 * Created on February 15, 2014, 2:37 PM
 */

#include <ogli/util.h>
#include <glm/gtx/projection.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtx/transform2.hpp>
#include <glm/gtc/random.hpp>

#include "Renderer.h"
#include "Vertex.h"
#include "Mesh.h"
#include "Texture2D.h"
#include "Model.h"

namespace mo {

    Renderer::Renderer() :
    position_attribute_3P3N2UV_(0, 3, "position", sizeof (Vertex), sizeof (glm::vec3), 0),
    normal_attribute_3P3N2UV_(1, 3, "normal", sizeof (Vertex), sizeof (glm::vec3), sizeof (glm::vec3)),
    uv_attribute_3P3N2UV_(2, 2, "uv", sizeof (Vertex), sizeof (glm::vec2), sizeof (glm::vec3) + sizeof (glm::vec3)) {
        ogli::init(); // Should this be done int ogli or mo?

        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
        glDepthMask(true);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        std::string standard_vertex_source = "#ifdef GL_ES\n"
                "precision mediump float;\n"
                "precision mediump int;\n"
                "#endif\n"
                "uniform mat4 model_view_projection;\n"
                "uniform mat4 model_view;\n"
                "attribute vec3 position;\n"
                "attribute vec3 normal;\n"
                "attribute vec2 uv;\n"
                "varying vec3 v_position;\n"
                "varying vec3 v_normal\n;"
                "varying vec3 v_position2;\n"
                "varying vec2 v_uv;\n"
                "void main()\n"
                "{\n"
                "    v_uv = uv;\n"
                "    v_position2 = normalize((model_view_projection * vec4(position, 1.0)).xyz);\n"
                "    v_position = (model_view * vec4(position, 0.0)).xyz;\n"
                "    v_normal = normalize((model_view * vec4(normal, 0.0)).xyz);\n"
                "    gl_Position = model_view_projection * vec4(position, 1.0);\n"
                "}\n";

        std::string standard_fragment_source = "#ifdef GL_ES\n"
                "precision mediump float;\n"
                "precision mediump int;\n"
                "#endif\n"
                "uniform vec4 color;\n"
                "uniform float opacity;\n"
                "uniform sampler2D texture;\n"
                "uniform vec3 light_position;\n"
                "varying vec3 v_position;\n"
                "varying vec3 v_normal;\n"
                "varying vec3 v_position2;\n"
                "varying vec2 v_uv;\n"
                "void main() {\n"
                "vec3 ambient = vec3(0.3, 0.3, 0.3);\n"
                "float d = (v_position.z + 100.0)/5000.0;\n"

                "vec3 light = normalize(light_position - v_position2);\n"
                "float intensity = max(dot(v_normal,light), 0.0) * 0.2;\n"

                "vec3 indirect = texture2D(texture, v_uv).rgb;\n"

                "gl_FragColor = vec4(indirect + intensity, 1.0*opacity);\n"
                "//gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"

                "}\n";

        addProgram("standard", standard_vertex_source, standard_fragment_source);

        std::string text_vertex_source = "#ifdef GL_ES\n"
                "precision mediump float;\n"
                "precision mediump int;\n"
                "#endif\n"
                "uniform mat4 model_view_projection;\n"
                "uniform mat4 model_view;\n"
                "attribute vec3 position;\n"
                "attribute vec2 uv;\n"

                "varying vec2 v_position;\n"
                "varying vec2 v_uv;\n"

                "void main(){\n"
                "v_position = (model_view * vec4(position, 0.0)).xy;\n"
                "v_uv = uv;\n"
                "gl_Position = model_view_projection * vec4(position, 1.0);\n"
                "}\n";

        std::string text_fragment_source = "#ifdef GL_ES\n"
                "precision mediump float;\n"
                "precision mediump int;\n"
                "#endif\n"
                "varying vec3 v_position;\n"
                "varying vec2 v_uv;\n"
                "uniform sampler2D texture;\n"
                "uniform float opacity;\n"

                "void main() {\n"
                "gl_FragColor = texture2D(texture, v_uv);\n"
                "gl_FragColor.w = gl_FragColor.w * opacity;\n"
                "//gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
                "}\n";
        addProgram("text", text_vertex_source, text_fragment_source);
    }

    Renderer::~Renderer() {
    }

    void Renderer::addProgram(const std::string path, const std::string vertex_shader_source, const std::string fragment_shader_source) {
        auto vertex_shader = ogli::createShader(vertex_shader_source, GL_VERTEX_SHADER);
        auto fragment_shader = ogli::createShader(fragment_shader_source, GL_FRAGMENT_SHADER);

        auto program = ogli::createProgram();
        ogli::attachShader(program, vertex_shader);
        ogli::attachShader(program, fragment_shader);
        ogli::bindAttribute(program, position_attribute_3P3N2UV_);
        ogli::bindAttribute(program, normal_attribute_3P3N2UV_);
        ogli::bindAttribute(program, uv_attribute_3P3N2UV_);

        ogli::linkProgram(program);

        auto mvp_uniform = ogli::createUniform(program, "model_view_projection");
        auto mv_uniform = ogli::createUniform(program, "model_view");
        auto texture_uniform = ogli::createUniform(program, "texture");
        auto opacity_uniform = ogli::createUniform(program, "opacity");

        programs_.insert(ProgramPair(path, ProgramData{program, mvp_uniform, mv_uniform, texture_uniform, opacity_uniform}));
    }

    void Renderer::addProgram(const std::string path) {
        addProgram(path, ogli::loadText(path + ".vs"), ogli::loadText(path + ".fs"));
    }

    void Renderer::clear(const glm::vec3 color) {
        ogli::clearDepth(1.0f);
        ogli::clearColor(glm::vec4(color.r, color.g, color.b, 0.0f));
    }

    void Renderer::render(const Model & model, const glm::mat4 transform, const glm::mat4 view, const glm::mat4 projection, const float opacity, const std::string program_name, const glm::vec3 light_position) {
        if (array_buffers_.find(model.mesh->id()) == array_buffers_.end()) {
            array_buffers_.insert(ArrayPair(model.mesh->id(),
                    ogli::createArrayBuffer(model.mesh->verticesBegin(), model.mesh->verticesEnd())));
        }
        if (element_array_buffers_.find(model.mesh->id()) == element_array_buffers_.end()) {
            element_array_buffers_.insert(ElementPair(model.mesh->id(),
                    ogli::createElementArrayBuffer(model.mesh->elementsBegin(), model.mesh->elementsEnd())));
        }

        if (textures_.find(model.texture->id()) == textures_.end()) {
            ogli::TextureBuffer texture = ogli::createTexture(model.texture->begin(), model.texture->end(), model.texture->width(), model.texture->height());
            textures_.insert(std::pair<unsigned int, ogli::TextureBuffer>(model.texture->id(), texture));
        }

        glm::mat4 mv = projection * view;
        glm::mat4 mvp = projection * view * transform * model.transform();

        //ogli::useProgram(standard_program_);

        ogli::useProgram(programs_.at(program_name).program);

        //ogli::useProgram(std::get<0>(programs_.at(program_name)));
        ogli::bindBuffer(array_buffers_.at(model.mesh->id()));
        ogli::bindBuffer(element_array_buffers_.at(model.mesh->id()));

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, textures_.at(model.texture->id()));

        //TODO: General lighting, this is a hack.
        glm::mat3 rot_mat(view);
        glm::vec3 dir(view[3]);

        glm::vec3 camera_pos = -dir * rot_mat;

        ogli::uniform(programs_.at(program_name).mvp, mvp);
        ogli::uniform(programs_.at(program_name).mv, mv);
        ogli::uniform(programs_.at(program_name).texture);
        ogli::uniform(programs_.at(program_name).opacity, opacity);
        ogli::uniform(programs_.at(program_name).light_position, light_position);

        ogli::attribute(position_attribute_3P3N2UV_);
        ogli::attribute(normal_attribute_3P3N2UV_);
        ogli::attribute(uv_attribute_3P3N2UV_);
        int num_elements = std::distance(model.mesh->elementsBegin(), model.mesh->elementsEnd());
        if (num_elements > 0){
            ogli::drawElements(num_elements);
        }
        else {
            ogli::drawArrays(std::distance(model.mesh->verticesBegin(), model.mesh->verticesEnd()));
        }
        if (!model.valid()) {
            ogli::updateArrayBuffer(array_buffers_.at(model.mesh->id()), model.mesh->verticesBegin(), model.mesh->verticesEnd());
        }
    }

}<commit_msg>Light stuff.#<commit_after>/*
 * File:   Renderer.cpp
 * Author: morgan
 *
 * Created on February 15, 2014, 2:37 PM
 */

#include <ogli/util.h>
#include <glm/gtx/projection.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtx/transform2.hpp>
#include <glm/gtc/random.hpp>

#include "Renderer.h"
#include "Vertex.h"
#include "Mesh.h"
#include "Texture2D.h"
#include "Model.h"

namespace mo {

    Renderer::Renderer() :
    position_attribute_3P3N2UV_(0, 3, "position", sizeof (Vertex), sizeof (glm::vec3), 0),
    normal_attribute_3P3N2UV_(1, 3, "normal", sizeof (Vertex), sizeof (glm::vec3), sizeof (glm::vec3)),
    uv_attribute_3P3N2UV_(2, 2, "uv", sizeof (Vertex), sizeof (glm::vec2), sizeof (glm::vec3) + sizeof (glm::vec3)) {
        ogli::init(); // Should this be done int ogli or mo?

        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
        glDepthMask(true);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        std::string standard_vertex_source = "#ifdef GL_ES\n"
                "precision mediump float;\n"
                "precision mediump int;\n"
                "#endif\n"
                "uniform mat4 model_view_projection;\n"
                "uniform mat4 model_view;\n"
                "attribute vec3 position;\n"
                "attribute vec3 normal;\n"
                "attribute vec2 uv;\n"
                "varying vec3 fragment_position;\n"
                "varying vec3 fragment_normal\n;"
                "varying vec2 fragment_uv;\n"
                "varying mat3 normal_matrix;\n"
                "void main()\n"
                "{\n"
                "    fragment_uv = uv;\n"
                "    fragment_position = (model_view * vec4(position, 0.0)).xyz;\n"
                "    normal_matrix = transpose(inverse(mat3(model_view)));\n"
                "    fragment_normal = normal_matrix * normal;\n"
                "    gl_Position = model_view_projection * vec4(position, 1.0);\n"
                "}\n";

        std::string standard_fragment_source = "#ifdef GL_ES\n"
                "precision mediump float;\n"
                "precision mediump int;\n"
                "#endif\n"
                "uniform vec4 color;\n"
                "uniform float opacity;\n"
                "uniform sampler2D texture;\n"
                "uniform vec3 light_position;\n"
                "varying vec3 fragment_position;\n"
                "varying vec3 fragment_normal;\n"
                "varying vec2 fragment_uv;\n"

                "void main() {\n"
                    "vec3 normal = normalize(fragment_normal);\n"
                    "vec3 surface_to_light = light_position - fragment_position;\n"
                    "float intensity = dot(normal, surface_to_light) / (length(surface_to_light) * length(normal));\n"
                    "intensity = clamp(intensity, 0.0, 1.0);\n"
                    "vec3 diffuse = texture2D(texture, fragment_uv).rgb;\n"

                    "gl_FragColor = vec4(diffuse * intensity, 1.0*opacity);\n"
                "}\n";

        addProgram("standard", standard_vertex_source, standard_fragment_source);

        std::string text_vertex_source = "#ifdef GL_ES\n"
                "precision mediump float;\n"
                "precision mediump int;\n"
                "#endif\n"
                "uniform mat4 model_view_projection;\n"
                "uniform mat4 model_view;\n"
                "attribute vec3 position;\n"
                "attribute vec2 uv;\n"

                "varying vec2 v_position;\n"
                "varying vec2 v_uv;\n"

                "void main(){\n"
                "v_position = (model_view * vec4(position, 0.0)).xy;\n"
                "v_uv = uv;\n"
                "gl_Position = model_view_projection * vec4(position, 1.0);\n"
                "}\n";

        std::string text_fragment_source = "#ifdef GL_ES\n"
                "precision mediump float;\n"
                "precision mediump int;\n"
                "#endif\n"
                "varying vec3 v_position;\n"
                "varying vec2 v_uv;\n"
                "uniform sampler2D texture;\n"
                "uniform float opacity;\n"

                "void main() {\n"
                "gl_FragColor = texture2D(texture, v_uv);\n"
                "gl_FragColor.w = gl_FragColor.w * opacity;\n"
                "//gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
                "}\n";
        addProgram("text", text_vertex_source, text_fragment_source);
    }

    Renderer::~Renderer() {
    }

    void Renderer::addProgram(const std::string path, const std::string vertex_shader_source, const std::string fragment_shader_source) {
        auto vertex_shader = ogli::createShader(vertex_shader_source, GL_VERTEX_SHADER);
        auto fragment_shader = ogli::createShader(fragment_shader_source, GL_FRAGMENT_SHADER);

        auto program = ogli::createProgram();
        ogli::attachShader(program, vertex_shader);
        ogli::attachShader(program, fragment_shader);
        ogli::bindAttribute(program, position_attribute_3P3N2UV_);
        ogli::bindAttribute(program, normal_attribute_3P3N2UV_);
        ogli::bindAttribute(program, uv_attribute_3P3N2UV_);

        ogli::linkProgram(program);

        auto mvp_uniform = ogli::createUniform(program, "model_view_projection");
        auto mv_uniform = ogli::createUniform(program, "model_view");
        auto texture_uniform = ogli::createUniform(program, "texture");
        auto opacity_uniform = ogli::createUniform(program, "opacity");
        auto light_uniform = ogli::createUniform(program, "light_position");

        programs_.insert(ProgramPair(path, ProgramData{program, mvp_uniform, mv_uniform, texture_uniform, opacity_uniform, light_uniform}));
    }

    void Renderer::addProgram(const std::string path) {
        addProgram(path, ogli::loadText(path + ".vs"), ogli::loadText(path + ".fs"));
    }

    void Renderer::clear(const glm::vec3 color) {
        ogli::clearDepth(1.0f);
        ogli::clearColor(glm::vec4(color.r, color.g, color.b, 0.0f));
    }

    void Renderer::render(const Model & model, const glm::mat4 transform, const glm::mat4 view, const glm::mat4 projection, const float opacity, const std::string program_name, const glm::vec3 light_position) {
        if (array_buffers_.find(model.mesh->id()) == array_buffers_.end()) {
            array_buffers_.insert(ArrayPair(model.mesh->id(),
                    ogli::createArrayBuffer(model.mesh->verticesBegin(), model.mesh->verticesEnd())));
        }
        if (element_array_buffers_.find(model.mesh->id()) == element_array_buffers_.end()) {
            element_array_buffers_.insert(ElementPair(model.mesh->id(),
                    ogli::createElementArrayBuffer(model.mesh->elementsBegin(), model.mesh->elementsEnd())));
        }

        if (textures_.find(model.texture->id()) == textures_.end()) {
            ogli::TextureBuffer texture = ogli::createTexture(model.texture->begin(), model.texture->end(), model.texture->width(), model.texture->height());
            textures_.insert(std::pair<unsigned int, ogli::TextureBuffer>(model.texture->id(), texture));
        }

        glm::mat4 mv = view * transform * model.transform();
        glm::mat4 mvp = projection * view * transform * model.transform();

        //ogli::useProgram(standard_program_);

        ogli::useProgram(programs_.at(program_name).program);

        //ogli::useProgram(std::get<0>(programs_.at(program_name)));
        ogli::bindBuffer(array_buffers_.at(model.mesh->id()));
        ogli::bindBuffer(element_array_buffers_.at(model.mesh->id()));

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, textures_.at(model.texture->id()));

        ogli::uniform(programs_.at(program_name).mvp, mvp);
        ogli::uniform(programs_.at(program_name).mv, mv);
        ogli::uniform(programs_.at(program_name).texture);
        ogli::uniform(programs_.at(program_name).opacity, opacity);
        ogli::uniform(programs_.at(program_name).light_position, light_position);

        ogli::attribute(position_attribute_3P3N2UV_);
        ogli::attribute(normal_attribute_3P3N2UV_);
        ogli::attribute(uv_attribute_3P3N2UV_);
        int num_elements = std::distance(model.mesh->elementsBegin(), model.mesh->elementsEnd());
        if (num_elements > 0){
            ogli::drawElements(num_elements);
        }
        else {
            ogli::drawArrays(std::distance(model.mesh->verticesBegin(), model.mesh->verticesEnd()));
        }
        if (!model.valid()) {
            ogli::updateArrayBuffer(array_buffers_.at(model.mesh->id()), model.mesh->verticesBegin(), model.mesh->verticesEnd());
        }
    }

}<|endoftext|>


<commit_before>// Scintilla source code edit control
/** @file LexHex.cxx
 ** Lexer for Motorola S-Record.
 **
 ** Written by Markus Heidelberg
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

/*
 *  Motorola S-Record file format
 * ===============================
 *
 * Each record (line) is built as follows:
 *
 *    field       size     states
 *
 *  +----------+
 *  | start    |  1 ('S')  SCE_SREC_RECSTART
 *  +----------+
 *  | type     |  1        SCE_SREC_RECTYPE
 *  +----------+
 *  | count    |  2        SCE_SREC_BYTECOUNT, SCE_SREC_BYTECOUNT_WRONG
 *  +----------+
 *  | address  |  4/6/8    SCE_SREC_NOADDRESS, SCE_SREC_DATAADDRESS, SCE_SREC_RECCOUNT, SCE_SREC_STARTADDRESS, (SCE_SREC_ADDRESSFIELD_UNKNOWN)
 *  +----------+
 *  | data     |  0..500   SCE_SREC_DATA_ODD, SCE_SREC_DATA_EVEN, (SCE_SREC_DATA_UNKNOWN)
 *  +----------+
 *  | checksum |  2        SCE_SREC_CHECKSUM, SCE_SREC_CHECKSUM_WRONG
 *  +----------+
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>

#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"

#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
#include "LexerModule.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

// prototypes for general helper functions
static inline bool IsNewline(const int ch);
static int GetHexaChar(char hd1, char hd2);
static int GetHexaChar(unsigned int pos, Accessor &styler);
static bool ForwardWithinLine(StyleContext &sc, int nb = 1);

// prototypes for file format specific helper functions
static unsigned int GetSrecRecStartPosition(unsigned int pos, Accessor &styler);
static int GetSrecByteCount(unsigned int recStartPos, Accessor &styler);
static int CountSrecByteCount(unsigned int recStartPos, Accessor &styler);
static int GetSrecAddressFieldSize(unsigned int recStartPos, Accessor &styler);
static int GetSrecAddressFieldType(unsigned int recStartPos, Accessor &styler);
static int GetSrecChecksum(unsigned int recStartPos, Accessor &styler);
static int CalcSrecChecksum(unsigned int recStartPos, Accessor &styler);

static inline bool IsNewline(const int ch)
{
    return (ch == '\n' || ch == '\r');
}

static int GetHexaChar(char hd1, char hd2)
{
	int hexValue = 0;

	if (hd1 >= '0' && hd1 <= '9') {
		hexValue += 16 * (hd1 - '0');
	} else if (hd1 >= 'A' && hd1 <= 'F') {
		hexValue += 16 * (hd1 - 'A' + 10);
	} else if (hd1 >= 'a' && hd1 <= 'f') {
		hexValue += 16 * (hd1 - 'a' + 10);
	} else {
		return -1;
	}

	if (hd2 >= '0' && hd2 <= '9') {
		hexValue += hd2 - '0';
	} else if (hd2 >= 'A' && hd2 <= 'F') {
		hexValue += hd2 - 'A' + 10;
	} else if (hd2 >= 'a' && hd2 <= 'f') {
		hexValue += hd2 - 'a' + 10;
	} else {
		return -1;
	}

	return hexValue;
}

static int GetHexaChar(unsigned int pos, Accessor &styler)
{
	char highNibble, lowNibble;

	highNibble = styler.SafeGetCharAt(pos);
	lowNibble = styler.SafeGetCharAt(pos + 1);

	return GetHexaChar(highNibble, lowNibble);
}

// Forward <nb> characters, but abort (and return false) if hitting the line
// end. Return true if forwarding within the line was possible.
// Avoids influence on highlighting of the subsequent line if the current line
// is malformed (too short).
static bool ForwardWithinLine(StyleContext &sc, int nb)
{
	for (int i = 0; i < nb; i++) {
		if (sc.atLineEnd) {
			// line is too short
			sc.SetState(SCE_SREC_DEFAULT);
			sc.Forward();
			return false;
		} else {
			sc.Forward();
		}
	}

	return true;
}

// Get the position of the record "start" field (first character in line) in
// the record around position <pos>.
static unsigned int GetSrecRecStartPosition(unsigned int pos, Accessor &styler)
{
	while (styler.SafeGetCharAt(pos) != 'S') {
		pos--;
	}

	return pos;
}

// Get the value of the "byte count" field, it counts the number of bytes in
// the subsequent fields ("address", "data" and "checksum" fields).
static int GetSrecByteCount(unsigned int recStartPos, Accessor &styler)
{
	int val;

	val = GetHexaChar(recStartPos + 2, styler);
	if (val < 0) {
	       val = 0;
	}

	return val;
}

// Count the number of digit pairs for the "address", "data" and "checksum"
// fields in this record. Has to be equal to the "byte count" field value.
// If the record is too short, a negative count may be returned.
static int CountSrecByteCount(unsigned int recStartPos, Accessor &styler)
{
	int cnt;
	unsigned int pos;

	pos = recStartPos;

	while (!IsNewline(styler.SafeGetCharAt(pos, '\n'))) {
		pos++;
	}

	// number of digits in this line minus number of digits of uncounted fields
	cnt = static_cast<int>(pos - recStartPos) - 4;

	// Prepare round up if odd (digit pair incomplete), this way the byte
	// count is considered to be valid if the checksum is incomplete.
	if (cnt >= 0) {
		cnt++;
	}

	// digit pairs
	cnt /= 2;

	return cnt;
}

// Get the size of the "address" field.
static int GetSrecAddressFieldSize(unsigned int recStartPos, Accessor &styler)
{
	switch (styler.SafeGetCharAt(recStartPos + 1)) {
		case '0':
		case '1':
		case '5':
		case '9':
			return 2; // 16 bit

		case '2':
		case '6':
		case '8':
			return 3; // 24 bit

		case '3':
		case '7':
			return 4; // 32 bit

		default:
			return 0;
	}
}

// Get the type of the "address" field content.
static int GetSrecAddressFieldType(unsigned int recStartPos, Accessor &styler)
{
	switch (styler.SafeGetCharAt(recStartPos + 1)) {
		case '0':
			return SCE_SREC_NOADDRESS;

		case '1':
		case '2':
		case '3':
			return SCE_SREC_DATAADDRESS;

		case '5':
		case '6':
			return SCE_SREC_RECCOUNT;

		case '7':
		case '8':
		case '9':
			return SCE_SREC_STARTADDRESS;

		default: // handle possible format extension in the future
			return SCE_SREC_ADDRESSFIELD_UNKNOWN;
	}
}

// Get the value of the "checksum" field.
static int GetSrecChecksum(unsigned int recStartPos, Accessor &styler)
{
	int byteCount;

	byteCount = GetSrecByteCount(recStartPos, styler);

	return GetHexaChar(recStartPos + 2 + byteCount * 2, styler);
}

// Calculate the checksum of the record.
static int CalcSrecChecksum(unsigned int recStartPos, Accessor &styler)
{
	int byteCount;
	unsigned int cs = 0;

	byteCount = GetSrecByteCount(recStartPos, styler);

	// sum over "byte count", "address" and "data" fields
	for (unsigned int pos = recStartPos + 2; pos < recStartPos + 2 + byteCount * 2; pos += 2) {
		int val = GetHexaChar(pos, styler);

		if (val < 0) {
			return val;
		}

		cs += val;
	}

	// low byte of one's complement
	return (~cs) & 0xFF;
}

static void ColouriseSrecDoc(unsigned int startPos, int length, int initStyle, WordList *[], Accessor &styler)
{
	StyleContext sc(startPos, length, initStyle, styler);

	while (sc.More()) {
		unsigned int recStartPos;
		int byteCount, addrFieldSize, addrFieldType, dataFieldSize;
		int cs1, cs2;

		switch (sc.state) {
			case SCE_SREC_DEFAULT:
				if (sc.atLineStart && sc.Match('S')) {
					sc.SetState(SCE_SREC_RECSTART);
				}
				ForwardWithinLine(sc);
				break;

			case SCE_SREC_RECSTART:
				sc.SetState(SCE_SREC_RECTYPE);
				ForwardWithinLine(sc);
				break;

			case SCE_SREC_RECTYPE:
				recStartPos = sc.currentPos - 2;
				byteCount = GetSrecByteCount(recStartPos, styler);

				if (byteCount == CountSrecByteCount(recStartPos, styler)) {
					sc.SetState(SCE_SREC_BYTECOUNT);
				} else {
					sc.SetState(SCE_SREC_BYTECOUNT_WRONG);
				}

				ForwardWithinLine(sc, 2);
				break;

			case SCE_SREC_BYTECOUNT:
			case SCE_SREC_BYTECOUNT_WRONG:
				recStartPos = sc.currentPos - 4;
				addrFieldSize = GetSrecAddressFieldSize(recStartPos, styler);
				addrFieldType = GetSrecAddressFieldType(recStartPos, styler);

				sc.SetState(addrFieldType);
				ForwardWithinLine(sc, addrFieldSize * 2);
				break;

			case SCE_SREC_NOADDRESS:
			case SCE_SREC_DATAADDRESS:
			case SCE_SREC_RECCOUNT:
			case SCE_SREC_STARTADDRESS:
			case SCE_SREC_ADDRESSFIELD_UNKNOWN:
				recStartPos = GetSrecRecStartPosition(sc.currentPos, styler);
				byteCount = GetSrecByteCount(recStartPos, styler);
				addrFieldSize = GetSrecAddressFieldSize(recStartPos, styler);
				dataFieldSize = byteCount - addrFieldSize - 1; // -1 for checksum field

				if (sc.state == SCE_SREC_ADDRESSFIELD_UNKNOWN) {
					sc.SetState(SCE_SREC_DATA_UNKNOWN);
					ForwardWithinLine(sc, dataFieldSize * 2);
					break;
				}

				sc.SetState(SCE_SREC_DATA_ODD);

				for (int i = 0; i < dataFieldSize * 2; i++) {
					if ((i & 0x3) == 0) {
						sc.SetState(SCE_SREC_DATA_ODD);
					} else if ((i & 0x3) == 2) {
						sc.SetState(SCE_SREC_DATA_EVEN);
					}

					if (!ForwardWithinLine(sc)) {
						break;
					}
				}
				break;

			case SCE_SREC_DATA_ODD:
			case SCE_SREC_DATA_EVEN:
			case SCE_SREC_DATA_UNKNOWN:
				recStartPos = GetSrecRecStartPosition(sc.currentPos, styler);
				cs1 = CalcSrecChecksum(recStartPos, styler);
				cs2 = GetSrecChecksum(recStartPos, styler);

				if (cs1 != cs2 || cs1 < 0 || cs2 < 0) {
					sc.SetState(SCE_SREC_CHECKSUM_WRONG);
				} else {
					sc.SetState(SCE_SREC_CHECKSUM);
				}

				ForwardWithinLine(sc, 2);
				break;

			case SCE_SREC_CHECKSUM:
			case SCE_SREC_CHECKSUM_WRONG:
				// record finished
				sc.SetState(SCE_SREC_DEFAULT);
				ForwardWithinLine(sc);
				break;
		}
	}
	sc.Complete();
}

LexerModule lmSrec(SCLEX_SREC, ColouriseSrecDoc, "srec", 0, NULL);
<commit_msg>S-Record lexer: fix the description of the maximum data field digit count 500 was only true for S3 records with 32-bit address.<commit_after>// Scintilla source code edit control
/** @file LexHex.cxx
 ** Lexer for Motorola S-Record.
 **
 ** Written by Markus Heidelberg
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

/*
 *  Motorola S-Record file format
 * ===============================
 *
 * Each record (line) is built as follows:
 *
 *    field       digits          states
 *
 *  +----------+
 *  | start    |  1 ('S')         SCE_SREC_RECSTART
 *  +----------+
 *  | type     |  1               SCE_SREC_RECTYPE
 *  +----------+
 *  | count    |  2               SCE_SREC_BYTECOUNT, SCE_SREC_BYTECOUNT_WRONG
 *  +----------+
 *  | address  |  4/6/8           SCE_SREC_NOADDRESS, SCE_SREC_DATAADDRESS, SCE_SREC_RECCOUNT, SCE_SREC_STARTADDRESS, (SCE_SREC_ADDRESSFIELD_UNKNOWN)
 *  +----------+
 *  | data     |  0..504/502/500  SCE_SREC_DATA_ODD, SCE_SREC_DATA_EVEN, (SCE_SREC_DATA_UNKNOWN)
 *  +----------+
 *  | checksum |  2               SCE_SREC_CHECKSUM, SCE_SREC_CHECKSUM_WRONG
 *  +----------+
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>

#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"

#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
#include "LexerModule.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

// prototypes for general helper functions
static inline bool IsNewline(const int ch);
static int GetHexaChar(char hd1, char hd2);
static int GetHexaChar(unsigned int pos, Accessor &styler);
static bool ForwardWithinLine(StyleContext &sc, int nb = 1);

// prototypes for file format specific helper functions
static unsigned int GetSrecRecStartPosition(unsigned int pos, Accessor &styler);
static int GetSrecByteCount(unsigned int recStartPos, Accessor &styler);
static int CountSrecByteCount(unsigned int recStartPos, Accessor &styler);
static int GetSrecAddressFieldSize(unsigned int recStartPos, Accessor &styler);
static int GetSrecAddressFieldType(unsigned int recStartPos, Accessor &styler);
static int GetSrecChecksum(unsigned int recStartPos, Accessor &styler);
static int CalcSrecChecksum(unsigned int recStartPos, Accessor &styler);

static inline bool IsNewline(const int ch)
{
    return (ch == '\n' || ch == '\r');
}

static int GetHexaChar(char hd1, char hd2)
{
	int hexValue = 0;

	if (hd1 >= '0' && hd1 <= '9') {
		hexValue += 16 * (hd1 - '0');
	} else if (hd1 >= 'A' && hd1 <= 'F') {
		hexValue += 16 * (hd1 - 'A' + 10);
	} else if (hd1 >= 'a' && hd1 <= 'f') {
		hexValue += 16 * (hd1 - 'a' + 10);
	} else {
		return -1;
	}

	if (hd2 >= '0' && hd2 <= '9') {
		hexValue += hd2 - '0';
	} else if (hd2 >= 'A' && hd2 <= 'F') {
		hexValue += hd2 - 'A' + 10;
	} else if (hd2 >= 'a' && hd2 <= 'f') {
		hexValue += hd2 - 'a' + 10;
	} else {
		return -1;
	}

	return hexValue;
}

static int GetHexaChar(unsigned int pos, Accessor &styler)
{
	char highNibble, lowNibble;

	highNibble = styler.SafeGetCharAt(pos);
	lowNibble = styler.SafeGetCharAt(pos + 1);

	return GetHexaChar(highNibble, lowNibble);
}

// Forward <nb> characters, but abort (and return false) if hitting the line
// end. Return true if forwarding within the line was possible.
// Avoids influence on highlighting of the subsequent line if the current line
// is malformed (too short).
static bool ForwardWithinLine(StyleContext &sc, int nb)
{
	for (int i = 0; i < nb; i++) {
		if (sc.atLineEnd) {
			// line is too short
			sc.SetState(SCE_SREC_DEFAULT);
			sc.Forward();
			return false;
		} else {
			sc.Forward();
		}
	}

	return true;
}

// Get the position of the record "start" field (first character in line) in
// the record around position <pos>.
static unsigned int GetSrecRecStartPosition(unsigned int pos, Accessor &styler)
{
	while (styler.SafeGetCharAt(pos) != 'S') {
		pos--;
	}

	return pos;
}

// Get the value of the "byte count" field, it counts the number of bytes in
// the subsequent fields ("address", "data" and "checksum" fields).
static int GetSrecByteCount(unsigned int recStartPos, Accessor &styler)
{
	int val;

	val = GetHexaChar(recStartPos + 2, styler);
	if (val < 0) {
	       val = 0;
	}

	return val;
}

// Count the number of digit pairs for the "address", "data" and "checksum"
// fields in this record. Has to be equal to the "byte count" field value.
// If the record is too short, a negative count may be returned.
static int CountSrecByteCount(unsigned int recStartPos, Accessor &styler)
{
	int cnt;
	unsigned int pos;

	pos = recStartPos;

	while (!IsNewline(styler.SafeGetCharAt(pos, '\n'))) {
		pos++;
	}

	// number of digits in this line minus number of digits of uncounted fields
	cnt = static_cast<int>(pos - recStartPos) - 4;

	// Prepare round up if odd (digit pair incomplete), this way the byte
	// count is considered to be valid if the checksum is incomplete.
	if (cnt >= 0) {
		cnt++;
	}

	// digit pairs
	cnt /= 2;

	return cnt;
}

// Get the size of the "address" field.
static int GetSrecAddressFieldSize(unsigned int recStartPos, Accessor &styler)
{
	switch (styler.SafeGetCharAt(recStartPos + 1)) {
		case '0':
		case '1':
		case '5':
		case '9':
			return 2; // 16 bit

		case '2':
		case '6':
		case '8':
			return 3; // 24 bit

		case '3':
		case '7':
			return 4; // 32 bit

		default:
			return 0;
	}
}

// Get the type of the "address" field content.
static int GetSrecAddressFieldType(unsigned int recStartPos, Accessor &styler)
{
	switch (styler.SafeGetCharAt(recStartPos + 1)) {
		case '0':
			return SCE_SREC_NOADDRESS;

		case '1':
		case '2':
		case '3':
			return SCE_SREC_DATAADDRESS;

		case '5':
		case '6':
			return SCE_SREC_RECCOUNT;

		case '7':
		case '8':
		case '9':
			return SCE_SREC_STARTADDRESS;

		default: // handle possible format extension in the future
			return SCE_SREC_ADDRESSFIELD_UNKNOWN;
	}
}

// Get the value of the "checksum" field.
static int GetSrecChecksum(unsigned int recStartPos, Accessor &styler)
{
	int byteCount;

	byteCount = GetSrecByteCount(recStartPos, styler);

	return GetHexaChar(recStartPos + 2 + byteCount * 2, styler);
}

// Calculate the checksum of the record.
static int CalcSrecChecksum(unsigned int recStartPos, Accessor &styler)
{
	int byteCount;
	unsigned int cs = 0;

	byteCount = GetSrecByteCount(recStartPos, styler);

	// sum over "byte count", "address" and "data" fields (6..510 digits)
	for (unsigned int pos = recStartPos + 2; pos < recStartPos + 2 + byteCount * 2; pos += 2) {
		int val = GetHexaChar(pos, styler);

		if (val < 0) {
			return val;
		}

		cs += val;
	}

	// low byte of one's complement
	return (~cs) & 0xFF;
}

static void ColouriseSrecDoc(unsigned int startPos, int length, int initStyle, WordList *[], Accessor &styler)
{
	StyleContext sc(startPos, length, initStyle, styler);

	while (sc.More()) {
		unsigned int recStartPos;
		int byteCount, addrFieldSize, addrFieldType, dataFieldSize;
		int cs1, cs2;

		switch (sc.state) {
			case SCE_SREC_DEFAULT:
				if (sc.atLineStart && sc.Match('S')) {
					sc.SetState(SCE_SREC_RECSTART);
				}
				ForwardWithinLine(sc);
				break;

			case SCE_SREC_RECSTART:
				sc.SetState(SCE_SREC_RECTYPE);
				ForwardWithinLine(sc);
				break;

			case SCE_SREC_RECTYPE:
				recStartPos = sc.currentPos - 2;
				byteCount = GetSrecByteCount(recStartPos, styler);

				if (byteCount == CountSrecByteCount(recStartPos, styler)) {
					sc.SetState(SCE_SREC_BYTECOUNT);
				} else {
					sc.SetState(SCE_SREC_BYTECOUNT_WRONG);
				}

				ForwardWithinLine(sc, 2);
				break;

			case SCE_SREC_BYTECOUNT:
			case SCE_SREC_BYTECOUNT_WRONG:
				recStartPos = sc.currentPos - 4;
				addrFieldSize = GetSrecAddressFieldSize(recStartPos, styler);
				addrFieldType = GetSrecAddressFieldType(recStartPos, styler);

				sc.SetState(addrFieldType);
				ForwardWithinLine(sc, addrFieldSize * 2);
				break;

			case SCE_SREC_NOADDRESS:
			case SCE_SREC_DATAADDRESS:
			case SCE_SREC_RECCOUNT:
			case SCE_SREC_STARTADDRESS:
			case SCE_SREC_ADDRESSFIELD_UNKNOWN:
				recStartPos = GetSrecRecStartPosition(sc.currentPos, styler);
				byteCount = GetSrecByteCount(recStartPos, styler);
				addrFieldSize = GetSrecAddressFieldSize(recStartPos, styler);
				dataFieldSize = byteCount - addrFieldSize - 1; // -1 for checksum field

				if (sc.state == SCE_SREC_ADDRESSFIELD_UNKNOWN) {
					sc.SetState(SCE_SREC_DATA_UNKNOWN);
					ForwardWithinLine(sc, dataFieldSize * 2);
					break;
				}

				sc.SetState(SCE_SREC_DATA_ODD);

				for (int i = 0; i < dataFieldSize * 2; i++) {
					if ((i & 0x3) == 0) {
						sc.SetState(SCE_SREC_DATA_ODD);
					} else if ((i & 0x3) == 2) {
						sc.SetState(SCE_SREC_DATA_EVEN);
					}

					if (!ForwardWithinLine(sc)) {
						break;
					}
				}
				break;

			case SCE_SREC_DATA_ODD:
			case SCE_SREC_DATA_EVEN:
			case SCE_SREC_DATA_UNKNOWN:
				recStartPos = GetSrecRecStartPosition(sc.currentPos, styler);
				cs1 = CalcSrecChecksum(recStartPos, styler);
				cs2 = GetSrecChecksum(recStartPos, styler);

				if (cs1 != cs2 || cs1 < 0 || cs2 < 0) {
					sc.SetState(SCE_SREC_CHECKSUM_WRONG);
				} else {
					sc.SetState(SCE_SREC_CHECKSUM);
				}

				ForwardWithinLine(sc, 2);
				break;

			case SCE_SREC_CHECKSUM:
			case SCE_SREC_CHECKSUM_WRONG:
				// record finished
				sc.SetState(SCE_SREC_DEFAULT);
				ForwardWithinLine(sc);
				break;
		}
	}
	sc.Complete();
}

LexerModule lmSrec(SCLEX_SREC, ColouriseSrecDoc, "srec", 0, NULL);
<|endoftext|>