<commit_before>/**
 * @file llfloaterpostcard.cpp
 * @brief Postcard send floater, allows setting name, e-mail address, etc.
 *
 * $LicenseInfo:firstyear=2004&license=viewergpl$
 *
 * Copyright (c) 2004-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llfloaterpostcard.h"

#include "llfontgl.h"
#include "llsys.h"
#include "llgl.h"
#include "v3dmath.h"
#include "lldir.h"

#include "llagent.h"
#include "llui.h"
#include "lllineeditor.h"
#include "llbutton.h"
#include "lltexteditor.h"
#include "llfloaterreg.h"
#include "llviewercontrol.h"
#include "llviewernetwork.h"
#include "lluictrlfactory.h"
#include "lluploaddialog.h"
#include "llviewerstats.h"
#include "llviewerwindow.h"
#include "llstatusbar.h"
#include "llviewerregion.h"
#include "lleconomy.h"

#include "llgl.h"
#include "llglheaders.h"
#include "llimagejpeg.h"
#include "llimagej2c.h"
#include "llvfile.h"
#include "llvfs.h"
#include "llviewertexture.h"
#include "llassetuploadresponders.h"
#include "llagentui.h"

#include <boost/regex.hpp>  //boost.regex lib

///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------

///----------------------------------------------------------------------------
/// Class LLFloaterPostcard
///----------------------------------------------------------------------------

LLFloaterPostcard::LLFloaterPostcard(const LLSD& key)
:	LLFloater(key),
	mJPEGImage(NULL),
	mViewerImage(NULL),
	mHasFirstMsgFocus(false)
{
	//LLUICtrlFactory::getInstance()->buildFloater(this, "floater_postcard.xml");
}

// Destroys the object
LLFloaterPostcard::~LLFloaterPostcard()
{
	mJPEGImage = NULL; // deletes image
}

BOOL LLFloaterPostcard::postBuild()
{
	// pick up the user's up-to-date email address
	gAgent.sendAgentUserInfoRequest();

	childSetAction("cancel_btn", onClickCancel, this);
	childSetAction("send_btn", onClickSend, this);

	childDisable("from_form");

	std::string name_string;
	LLAgentUI::buildFullname(name_string);
	childSetValue("name_form", LLSD(name_string));

	// For the first time a user focusess to .the msg box, all text will be selected.
	getChild<LLUICtrl>("msg_form")->setFocusChangedCallback(boost::bind(onMsgFormFocusRecieved, _1, this));

	childSetFocus("to_form", TRUE);

    return TRUE;
}

// static
LLFloaterPostcard* LLFloaterPostcard::showFromSnapshot(LLImageJPEG *jpeg, LLViewerTexture *img, const LLVector2 &image_scale, const LLVector3d& pos_taken_global)
{
	// Take the images from the caller
	// It's now our job to clean them up
	LLFloaterPostcard* instance = LLFloaterReg::showTypedInstance<LLFloaterPostcard>("postcard", LLSD(img->getID()));

	instance->mJPEGImage = jpeg;
	instance->mViewerImage = img;
	instance->mImageScale = image_scale;
	instance->mPosTakenGlobal = pos_taken_global;

	return instance;
}

void LLFloaterPostcard::draw()
{
	LLGLSUIDefault gls_ui;
	LLFloater::draw();

	if(!isMinimized() && mViewerImage.notNull() && mJPEGImage.notNull())
	{
		LLRect rect(getRect());

		// first set the max extents of our preview
		rect.translate(-rect.mLeft, -rect.mBottom);
		rect.mLeft += 280;
		rect.mRight -= 10;
		rect.mTop -= 20;
		rect.mBottom = rect.mTop - 130;

		// then fix the aspect ratio
		F32 ratio = (F32)mJPEGImage->getWidth() / (F32)mJPEGImage->getHeight();
		if ((F32)rect.getWidth() / (F32)rect.getHeight() >= ratio)
		{
			rect.mRight = LLRect::tCoordType((F32)rect.mLeft + ((F32)rect.getHeight() * ratio));
		}
		else
		{
			rect.mBottom = LLRect::tCoordType((F32)rect.mTop - ((F32)rect.getWidth() / ratio));
		}
		{
			gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
			gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
			rect.stretch(-1);
		}
		{

		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		{
			glScalef(mImageScale.mV[VX], mImageScale.mV[VY], 1.f);
			glMatrixMode(GL_MODELVIEW);
			gl_draw_scaled_image(rect.mLeft,
								 rect.mBottom,
								 rect.getWidth(),
								 rect.getHeight(),
								 mViewerImage.get(),
								 LLColor4::white);
		}
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		}
	}
}

// static
void LLFloaterPostcard::onClickCancel(void* data)
{
	if (data)
	{
		LLFloaterPostcard *self = (LLFloaterPostcard *)data;

		self->closeFloater(false);
	}
}

class LLSendPostcardResponder : public LLAssetUploadResponder
{
public:
	LLSendPostcardResponder(const LLSD &post_data,
							const LLUUID& vfile_id,
							LLAssetType::EType asset_type):
	    LLAssetUploadResponder(post_data, vfile_id, asset_type)
	{
	}
	// *TODO define custom uploadFailed here so it's not such a generic message
	void uploadComplete(const LLSD& content)
	{
		// we don't care about what the server returns from this post, just clean up the UI
		LLUploadDialog::modalUploadFinished();
	}
};

// static
void LLFloaterPostcard::onClickSend(void* data)
{
	if (data)
	{
		LLFloaterPostcard *self = (LLFloaterPostcard *)data;

		std::string from(self->childGetValue("from_form").asString());
		std::string to(self->childGetValue("to_form").asString());

		boost::regex emailFormat("[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}(,[ \t]*[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,})*");

		if (to.empty() || !boost::regex_match(to, emailFormat))
		{
			LLNotifications::instance().add("PromptRecipientEmail");
			return;
		}

		if (from.empty() || !boost::regex_match(from, emailFormat))
		{
			LLNotifications::instance().add("PromptSelfEmail");
			return;
		}

		std::string subject(self->childGetValue("subject_form").asString());
		if(subject.empty() || !self->mHasFirstMsgFocus)
		{
			LLNotifications::instance().add("PromptMissingSubjMsg", LLSD(), LLSD(), boost::bind(&LLFloaterPostcard::missingSubjMsgAlertCallback, self, _1, _2));
			return;
		}

		if (self->mJPEGImage.notNull())
		{
			self->sendPostcard();
		}
		else
		{
			LLNotifications::instance().add("ErrorProcessingSnapshot");
		}
	}
}

// static
void LLFloaterPostcard::uploadCallback(const LLUUID& asset_id, void *user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed)
{
	LLFloaterPostcard *self = (LLFloaterPostcard *)user_data;

	LLUploadDialog::modalUploadFinished();

	if (result)
	{
		LLSD args;
		args["REASON"] = std::string(LLAssetStorage::getErrorString(result));
		LLNotifications::instance().add("ErrorUploadingPostcard", args);
	}
	else
	{
		// only create the postcard once the upload succeeds

		// request the postcard
		LLMessageSystem* msg = gMessageSystem;
		msg->newMessage("SendPostcard");
		msg->nextBlock("AgentData");
		msg->addUUID("AgentID", gAgent.getID());
		msg->addUUID("SessionID", gAgent.getSessionID());
		msg->addUUID("AssetID", self->mAssetID);
		msg->addVector3d("PosGlobal", self->mPosTakenGlobal);
		msg->addString("To", self->childGetValue("to_form").asString());
		msg->addString("From", self->childGetValue("from_form").asString());
		msg->addString("Name", self->childGetValue("name_form").asString());
		msg->addString("Subject", self->childGetValue("subject_form").asString());
		msg->addString("Msg", self->childGetValue("msg_form").asString());
		msg->addBOOL("AllowPublish", FALSE);
		msg->addBOOL("MaturePublish", FALSE);
		gAgent.sendReliableMessage();
	}

	self->closeFloater();
}

// static
void LLFloaterPostcard::updateUserInfo(const std::string& email)
{
	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel");
	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
		 iter != inst_list.end(); ++iter)
	{
		LLFloater* instance = *iter;
		const std::string& text = instance->childGetValue("from_form").asString();
		if (text.empty())
		{
			// there's no text in this field yet, pre-populate
			instance->childSetValue("from_form", LLSD(email));
		}
	}
}

void LLFloaterPostcard::onMsgFormFocusRecieved(LLFocusableElement* receiver, void* data)
{
	LLFloaterPostcard* self = (LLFloaterPostcard *)data;
	if(self)
	{
		LLTextEditor* msgForm = self->getChild<LLTextEditor>("msg_form");
		if(msgForm && msgForm == receiver && msgForm->hasFocus() && !(self->mHasFirstMsgFocus))
		{
			self->mHasFirstMsgFocus = true;
			msgForm->setText(LLStringUtil::null);
		}
	}
}

bool LLFloaterPostcard::missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotification::getSelectedOption(notification, response);
	if(0 == option)
	{
		// User clicked OK
		if((childGetValue("subject_form").asString()).empty())
		{
			// Stuff the subject back into the form.
			childSetValue("subject_form", getString("default_subject"));
		}

		if(!mHasFirstMsgFocus)
		{
			// The user never switched focus to the messagee window.
			// Using the default string.
			childSetValue("msg_form", getString("default_message"));
		}

		sendPostcard();
	}
	return false;
}

void LLFloaterPostcard::sendPostcard()
{
	mTransactionID.generate();
	mAssetID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
	LLVFile::writeFile(mJPEGImage->getData(), mJPEGImage->getDataSize(), gVFS, mAssetID, LLAssetType::AT_IMAGE_JPEG);

	// upload the image
	std::string url = gAgent.getRegion()->getCapability("SendPostcard");
	if(!url.empty())
	{
		llinfos << "Send Postcard via capability" << llendl;
		LLSD body = LLSD::emptyMap();
		// the capability already encodes: agent ID, region ID
		body["pos-global"] = mPosTakenGlobal.getValue();
		body["to"] = childGetValue("to_form").asString();
		body["from"] = childGetValue("from_form").asString();
		body["name"] = childGetValue("name_form").asString();
		body["subject"] = childGetValue("subject_form").asString();
		body["msg"] = childGetValue("msg_form").asString();
		LLHTTPClient::post(url, body, new LLSendPostcardResponder(body, mAssetID, LLAssetType::AT_IMAGE_JPEG));
	}
	else
	{
		gAssetStorage->storeAssetData(mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)this, FALSE);
	}

	// give user feedback of the event
	gViewerWindow->playSnapshotAnimAndSound();
	LLUploadDialog::modalUploadDialog(getString("upload_message"));

	// don't destroy the window until the upload is done
	// this way we keep the information in the form
	setVisible(FALSE);

	// also remove any dependency on another floater
	// so that we can be sure to outlive it while we
	// need to.
	LLFloater* dependee = getDependee();
	if (dependee)
		dependee->removeDependentFloater(this);
}
<commit_msg>DEV-42409 Email snapshot now fills in sender email address as before. Sending now works. Reviewed with Ambroff.<commit_after>/**
 * @file llfloaterpostcard.cpp
 * @brief Postcard send floater, allows setting name, e-mail address, etc.
 *
 * $LicenseInfo:firstyear=2004&license=viewergpl$
 *
 * Copyright (c) 2004-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llfloaterpostcard.h"

#include "llfontgl.h"
#include "llsys.h"
#include "llgl.h"
#include "v3dmath.h"
#include "lldir.h"

#include "llagent.h"
#include "llui.h"
#include "lllineeditor.h"
#include "llbutton.h"
#include "lltexteditor.h"
#include "llfloaterreg.h"
#include "llviewercontrol.h"
#include "llviewernetwork.h"
#include "lluictrlfactory.h"
#include "lluploaddialog.h"
#include "llviewerstats.h"
#include "llviewerwindow.h"
#include "llstatusbar.h"
#include "llviewerregion.h"
#include "lleconomy.h"

#include "llgl.h"
#include "llglheaders.h"
#include "llimagejpeg.h"
#include "llimagej2c.h"
#include "llvfile.h"
#include "llvfs.h"
#include "llviewertexture.h"
#include "llassetuploadresponders.h"
#include "llagentui.h"

#include <boost/regex.hpp>  //boost.regex lib

///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------

///----------------------------------------------------------------------------
/// Class LLFloaterPostcard
///----------------------------------------------------------------------------

LLFloaterPostcard::LLFloaterPostcard(const LLSD& key)
:	LLFloater(key),
	mJPEGImage(NULL),
	mViewerImage(NULL),
	mHasFirstMsgFocus(false)
{
	//LLUICtrlFactory::getInstance()->buildFloater(this, "floater_postcard.xml");
}

// Destroys the object
LLFloaterPostcard::~LLFloaterPostcard()
{
	mJPEGImage = NULL; // deletes image
}

BOOL LLFloaterPostcard::postBuild()
{
	// pick up the user's up-to-date email address
	gAgent.sendAgentUserInfoRequest();

	childSetAction("cancel_btn", onClickCancel, this);
	childSetAction("send_btn", onClickSend, this);

	childDisable("from_form");

	std::string name_string;
	LLAgentUI::buildFullname(name_string);
	childSetValue("name_form", LLSD(name_string));

	// For the first time a user focusess to .the msg box, all text will be selected.
	getChild<LLUICtrl>("msg_form")->setFocusChangedCallback(boost::bind(onMsgFormFocusRecieved, _1, this));

	childSetFocus("to_form", TRUE);

    return TRUE;
}

// static
LLFloaterPostcard* LLFloaterPostcard::showFromSnapshot(LLImageJPEG *jpeg, LLViewerTexture *img, const LLVector2 &image_scale, const LLVector3d& pos_taken_global)
{
	// Take the images from the caller
	// It's now our job to clean them up
	LLFloaterPostcard* instance = LLFloaterReg::showTypedInstance<LLFloaterPostcard>("postcard", LLSD(img->getID()));

	instance->mJPEGImage = jpeg;
	instance->mViewerImage = img;
	instance->mImageScale = image_scale;
	instance->mPosTakenGlobal = pos_taken_global;

	return instance;
}

void LLFloaterPostcard::draw()
{
	LLGLSUIDefault gls_ui;
	LLFloater::draw();

	if(!isMinimized() && mViewerImage.notNull() && mJPEGImage.notNull())
	{
		LLRect rect(getRect());

		// first set the max extents of our preview
		rect.translate(-rect.mLeft, -rect.mBottom);
		rect.mLeft += 280;
		rect.mRight -= 10;
		rect.mTop -= 20;
		rect.mBottom = rect.mTop - 130;

		// then fix the aspect ratio
		F32 ratio = (F32)mJPEGImage->getWidth() / (F32)mJPEGImage->getHeight();
		if ((F32)rect.getWidth() / (F32)rect.getHeight() >= ratio)
		{
			rect.mRight = LLRect::tCoordType((F32)rect.mLeft + ((F32)rect.getHeight() * ratio));
		}
		else
		{
			rect.mBottom = LLRect::tCoordType((F32)rect.mTop - ((F32)rect.getWidth() / ratio));
		}
		{
			gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
			gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
			rect.stretch(-1);
		}
		{

		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		{
			glScalef(mImageScale.mV[VX], mImageScale.mV[VY], 1.f);
			glMatrixMode(GL_MODELVIEW);
			gl_draw_scaled_image(rect.mLeft,
								 rect.mBottom,
								 rect.getWidth(),
								 rect.getHeight(),
								 mViewerImage.get(),
								 LLColor4::white);
		}
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		}
	}
}

// static
void LLFloaterPostcard::onClickCancel(void* data)
{
	if (data)
	{
		LLFloaterPostcard *self = (LLFloaterPostcard *)data;

		self->closeFloater(false);
	}
}

class LLSendPostcardResponder : public LLAssetUploadResponder
{
public:
	LLSendPostcardResponder(const LLSD &post_data,
							const LLUUID& vfile_id,
							LLAssetType::EType asset_type):
	    LLAssetUploadResponder(post_data, vfile_id, asset_type)
	{
	}
	// *TODO define custom uploadFailed here so it's not such a generic message
	void uploadComplete(const LLSD& content)
	{
		// we don't care about what the server returns from this post, just clean up the UI
		LLUploadDialog::modalUploadFinished();
	}
};

// static
void LLFloaterPostcard::onClickSend(void* data)
{
	if (data)
	{
		LLFloaterPostcard *self = (LLFloaterPostcard *)data;

		std::string from(self->childGetValue("from_form").asString());
		std::string to(self->childGetValue("to_form").asString());

		boost::regex emailFormat("[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}(,[ \t]*[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,})*");

		if (to.empty() || !boost::regex_match(to, emailFormat))
		{
			LLNotifications::instance().add("PromptRecipientEmail");
			return;
		}

		if (from.empty() || !boost::regex_match(from, emailFormat))
		{
			LLNotifications::instance().add("PromptSelfEmail");
			return;
		}

		std::string subject(self->childGetValue("subject_form").asString());
		if(subject.empty() || !self->mHasFirstMsgFocus)
		{
			LLNotifications::instance().add("PromptMissingSubjMsg", LLSD(), LLSD(), boost::bind(&LLFloaterPostcard::missingSubjMsgAlertCallback, self, _1, _2));
			return;
		}

		if (self->mJPEGImage.notNull())
		{
			self->sendPostcard();
		}
		else
		{
			LLNotifications::instance().add("ErrorProcessingSnapshot");
		}
	}
}

// static
void LLFloaterPostcard::uploadCallback(const LLUUID& asset_id, void *user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed)
{
	LLFloaterPostcard *self = (LLFloaterPostcard *)user_data;

	LLUploadDialog::modalUploadFinished();

	if (result)
	{
		LLSD args;
		args["REASON"] = std::string(LLAssetStorage::getErrorString(result));
		LLNotifications::instance().add("ErrorUploadingPostcard", args);
	}
	else
	{
		// only create the postcard once the upload succeeds

		// request the postcard
		LLMessageSystem* msg = gMessageSystem;
		msg->newMessage("SendPostcard");
		msg->nextBlock("AgentData");
		msg->addUUID("AgentID", gAgent.getID());
		msg->addUUID("SessionID", gAgent.getSessionID());
		msg->addUUID("AssetID", self->mAssetID);
		msg->addVector3d("PosGlobal", self->mPosTakenGlobal);
		msg->addString("To", self->childGetValue("to_form").asString());
		msg->addString("From", self->childGetValue("from_form").asString());
		msg->addString("Name", self->childGetValue("name_form").asString());
		msg->addString("Subject", self->childGetValue("subject_form").asString());
		msg->addString("Msg", self->childGetValue("msg_form").asString());
		msg->addBOOL("AllowPublish", FALSE);
		msg->addBOOL("MaturePublish", FALSE);
		gAgent.sendReliableMessage();
	}

	self->closeFloater();
}

// static
void LLFloaterPostcard::updateUserInfo(const std::string& email)
{
	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("postcard");
	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
		 iter != inst_list.end(); ++iter)
	{
		LLFloater* instance = *iter;
		const std::string& text = instance->childGetValue("from_form").asString();
		if (text.empty())
		{
			// there's no text in this field yet, pre-populate
			instance->childSetValue("from_form", LLSD(email));
		}
	}
}

void LLFloaterPostcard::onMsgFormFocusRecieved(LLFocusableElement* receiver, void* data)
{
	LLFloaterPostcard* self = (LLFloaterPostcard *)data;
	if(self)
	{
		LLTextEditor* msgForm = self->getChild<LLTextEditor>("msg_form");
		if(msgForm && msgForm == receiver && msgForm->hasFocus() && !(self->mHasFirstMsgFocus))
		{
			self->mHasFirstMsgFocus = true;
			msgForm->setText(LLStringUtil::null);
		}
	}
}

bool LLFloaterPostcard::missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotification::getSelectedOption(notification, response);
	if(0 == option)
	{
		// User clicked OK
		if((childGetValue("subject_form").asString()).empty())
		{
			// Stuff the subject back into the form.
			childSetValue("subject_form", getString("default_subject"));
		}

		if(!mHasFirstMsgFocus)
		{
			// The user never switched focus to the messagee window.
			// Using the default string.
			childSetValue("msg_form", getString("default_message"));
		}

		sendPostcard();
	}
	return false;
}

void LLFloaterPostcard::sendPostcard()
{
	mTransactionID.generate();
	mAssetID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
	LLVFile::writeFile(mJPEGImage->getData(), mJPEGImage->getDataSize(), gVFS, mAssetID, LLAssetType::AT_IMAGE_JPEG);

	// upload the image
	std::string url = gAgent.getRegion()->getCapability("SendPostcard");
	if(!url.empty())
	{
		llinfos << "Send Postcard via capability" << llendl;
		LLSD body = LLSD::emptyMap();
		// the capability already encodes: agent ID, region ID
		body["pos-global"] = mPosTakenGlobal.getValue();
		body["to"] = childGetValue("to_form").asString();
		body["from"] = childGetValue("from_form").asString();
		body["name"] = childGetValue("name_form").asString();
		body["subject"] = childGetValue("subject_form").asString();
		body["msg"] = childGetValue("msg_form").asString();
		LLHTTPClient::post(url, body, new LLSendPostcardResponder(body, mAssetID, LLAssetType::AT_IMAGE_JPEG));
	}
	else
	{
		gAssetStorage->storeAssetData(mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)this, FALSE);
	}

	// give user feedback of the event
	gViewerWindow->playSnapshotAnimAndSound();
	LLUploadDialog::modalUploadDialog(getString("upload_message"));

	// don't destroy the window until the upload is done
	// this way we keep the information in the form
	setVisible(FALSE);

	// also remove any dependency on another floater
	// so that we can be sure to outlive it while we
	// need to.
	LLFloater* dependee = getDependee();
	if (dependee)
		dependee->removeDependentFloater(this);
}
<|endoftext|>


<commit_before>#include <sstream>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/program_options.hpp>
#include <condition_variable>
#include <chrono>
#include <atomic>
#include <rados/librados.hpp>
#include "libzlog.h"

namespace po = boost::program_options;

#define VERIFY_IOS
#define SLOPPY_SEQ

struct AioState {
  zlog::Log *log;
  ceph::bufferlist append_bl;
  ceph::bufferlist read_bl;
  zlog::Log::AioCompletion *c;
  uint64_t position;
};

static std::condition_variable io_cond;
static std::mutex io_lock;
static std::atomic<uint64_t> outstanding_ios;
static std::atomic<uint64_t> ios_completed;

#ifdef VERIFY_IOS
static void verify_append_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;

  assert(s->c->get_return_value() == 0);
  s->c->release();

  assert(s->read_bl == s->append_bl);
  int sum1 = 0, sum2 = 0;
  for (unsigned i = 0; i < s->read_bl.length(); i++) {
    sum1 += (int)s->read_bl[i];
    sum2 += (int)s->append_bl[i];
  }
  assert(sum1 == sum2);

  delete s;
}
#endif

static void handle_append_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;
  zlog::Log *log = s->log;

  ios_completed++;
  outstanding_ios--;
  io_cond.notify_one();

  assert(s->c->get_return_value() == 0);
  s->c->release();
  s->c = NULL;

#ifdef VERIFY_IOS
  if (s->append_bl.length() > 0)
    assert(!(s->append_bl == s->read_bl));
  s->c = zlog::Log::aio_create_completion(s, verify_append_cb);
  int ret = log->AioRead(s->position, s->c, &s->read_bl);
  assert(ret == 0);
#else
  delete s;
#endif
}

class FakeSeqrClient : public zlog::SeqrClient {
 public:
  FakeSeqrClient(const std::string& name) :
    SeqrClient("", ""), seq_(0), name_(name)
  {
    assert(seq_.is_lock_free());
  }

  void Init(librados::IoCtx& ioctx) {
    zlog::Log log;
    int ret = zlog::Log::Open(ioctx, name_, NULL, log);
    assert(ret == 0);

    uint64_t epoch;
    ret = log.SetProjection(&epoch);
    assert(ret == 0);

    ret = log.Seal(epoch);
    assert(ret == 0);

    uint64_t position;
    ret = log.FindMaxPosition(epoch, &position);
    assert(ret == 0);

    epoch_ = epoch;
    seq_.store(position);

    std::cout << "init seq: pos=" << seq_.load() << std::endl;
  }

  void Connect() {}

  int CheckTail(uint64_t epoch, const std::string& pool,
      const std::string& name, uint64_t *position, bool next)
  {
    assert(name == name_);

    if (epoch_ < epoch)
      return -ERANGE;

    if (next) {
#ifdef SLOPPY_SEQ
      if (seq_ > 100 && seq_ % 10 == 0) {
        *position = seq_ / 2;
        seq_++;
      } else {
        uint64_t tail = seq_.fetch_add(1); // returns previous value
        *position = tail + 1;
      }
#else
      uint64_t tail = seq_.fetch_add(1); // returns previous value
      *position = tail + 1;
#endif
    } else
      *position = seq_.load();

    return 0;
  }

 private:
  std::atomic<uint64_t> seq_;
  std::string name_;
  uint64_t epoch_;
};

int main(int argc, char **argv)
{
  int width;
  std::string pool;
  std::string logname_req;
  int qdepth;
  int iosize;

  po::options_description desc("Allowed options");
  desc.add_options()
    ("pool", po::value<std::string>(&pool)->required(), "Pool name")
    ("logname", po::value<std::string>(&logname_req)->default_value(""), "Log name")
    ("width", po::value<int>(&width)->required(), "Width")
    ("qdepth", po::value<int>(&qdepth)->default_value(1), "Queue depth")
    ("iosize", po::value<int>(&iosize)->default_value(0), "IO Size")
  ;

  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);

  assert(qdepth > 0);

  // connect to rados
  librados::Rados cluster;
  cluster.init(NULL);
  cluster.conf_read_file(NULL);
  cluster.conf_parse_env(NULL);
  int ret = cluster.connect();
  assert(ret == 0);

  // open pool i/o context
  librados::IoCtx ioctx;
  ret = cluster.ioctx_create(pool.c_str(), ioctx);
  assert(ret == 0);

  // build a log name
  std::string logname;
  {
    std::stringstream ss;
    if (logname_req.size())
      ss << logname_req;
    else {
      boost::uuids::uuid uuid = boost::uuids::random_generator()();
      ss << uuid;
    }
    ss << ".log";
    logname = ss.str();
  }

  // setup log instance
  zlog::Log log;
  FakeSeqrClient client(logname);
  ret = zlog::Log::OpenOrCreate(ioctx, logname, width, &client, log);
  client.Init(ioctx);
  assert(ret == 0);

  // this is just a little hack that forces the epoch to be refreshed in the
  // log instance. otherwise when we blast out a bunch of async requests they
  // all end up having old epochs.
  ceph::bufferlist bl;
  log.Append(bl);

  std::unique_lock<std::mutex> lock(io_lock);

  ios_completed = 0;
  outstanding_ios = 0;

  char iobuf[iosize];

  auto start = std::chrono::steady_clock::now();
  for (;;) {
    uint64_t ios_completed_end = ios_completed;
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> diff = std::chrono::duration_cast<
      std::chrono::duration<double>>(end - start);
    double secs = diff.count();
    if (secs > 5) {
      double ios_per_sec = (double)ios_completed_end / secs;
      std::cout << ios_per_sec << std::endl;
      ios_completed = 0;
      start = std::chrono::steady_clock::now();
    }

    while (outstanding_ios < qdepth) {

      AioState *state = new AioState;

      for (unsigned i = 0; i < iosize; i++) {
        iobuf[i] = (char)rand();
      }

      state->append_bl.append(iobuf, iosize);
      state->log = &log;

      state->c = zlog::Log::aio_create_completion(state, handle_append_cb);
      assert(state->c);
      ret = log.AioAppend(state->c, state->append_bl, &state->position);
      assert(ret == 0);
      outstanding_ios++;
    }
    io_cond.wait(lock, [&]{ return outstanding_ios < qdepth; });
  }

  return 0;
}
<commit_msg>bench: track avg latency<commit_after>#include <sstream>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/program_options.hpp>
#include <condition_variable>
#include <chrono>
#include <atomic>
#include <rados/librados.hpp>
#include "libzlog.h"

namespace po = boost::program_options;

#define VERIFY_IOS
#define SLOPPY_SEQ

struct AioState {
  zlog::Log *log;
  ceph::bufferlist append_bl;
  ceph::bufferlist read_bl;
  zlog::Log::AioCompletion *c;
  uint64_t position;
  std::chrono::time_point<std::chrono::steady_clock> submitted;
};

static std::condition_variable io_cond;
static std::mutex io_lock;
static std::atomic<uint64_t> outstanding_ios;
static std::atomic<uint64_t> ios_completed;
static std::atomic<uint64_t> latency_ms;

#ifdef VERIFY_IOS
static void verify_append_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;

  assert(s->c->get_return_value() == 0);
  s->c->release();

  assert(s->read_bl == s->append_bl);
  int sum1 = 0, sum2 = 0;
  for (unsigned i = 0; i < s->read_bl.length(); i++) {
    sum1 += (int)s->read_bl[i];
    sum2 += (int)s->append_bl[i];
  }
  assert(sum1 == sum2);

  delete s;
}
#endif

static void handle_append_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;
  auto completed = std::chrono::steady_clock::now();
  zlog::Log *log = s->log;

  ios_completed++;
  outstanding_ios--;
  io_cond.notify_one();

  assert(s->c->get_return_value() == 0);
  s->c->release();
  s->c = NULL;

  auto diff_ms = std::chrono::duration_cast<
    std::chrono::milliseconds>(completed - s->submitted);
  latency_ms += diff_ms.count();

#ifdef VERIFY_IOS
  if (s->append_bl.length() > 0)
    assert(!(s->append_bl == s->read_bl));
  s->c = zlog::Log::aio_create_completion(s, verify_append_cb);
  int ret = log->AioRead(s->position, s->c, &s->read_bl);
  assert(ret == 0);
#else
  delete s;
#endif
}

class FakeSeqrClient : public zlog::SeqrClient {
 public:
  FakeSeqrClient(const std::string& name) :
    SeqrClient("", ""), seq_(0), name_(name)
  {
    assert(seq_.is_lock_free());
  }

  void Init(librados::IoCtx& ioctx) {
    zlog::Log log;
    int ret = zlog::Log::Open(ioctx, name_, NULL, log);
    assert(ret == 0);

    uint64_t epoch;
    ret = log.SetProjection(&epoch);
    assert(ret == 0);

    ret = log.Seal(epoch);
    assert(ret == 0);

    uint64_t position;
    ret = log.FindMaxPosition(epoch, &position);
    assert(ret == 0);

    epoch_ = epoch;
    seq_.store(position);

    std::cout << "init seq: pos=" << seq_.load() << std::endl;
  }

  void Connect() {}

  int CheckTail(uint64_t epoch, const std::string& pool,
      const std::string& name, uint64_t *position, bool next)
  {
    assert(name == name_);

    if (epoch_ < epoch)
      return -ERANGE;

    if (next) {
#ifdef SLOPPY_SEQ
      if (seq_ > 100 && seq_ % 10 == 0) {
        *position = seq_ / 2;
        seq_++;
      } else {
        uint64_t tail = seq_.fetch_add(1); // returns previous value
        *position = tail + 1;
      }
#else
      uint64_t tail = seq_.fetch_add(1); // returns previous value
      *position = tail + 1;
#endif
    } else
      *position = seq_.load();

    return 0;
  }

 private:
  std::atomic<uint64_t> seq_;
  std::string name_;
  uint64_t epoch_;
};

int main(int argc, char **argv)
{
  int width;
  std::string pool;
  std::string logname_req;
  int qdepth;
  int iosize;

  po::options_description desc("Allowed options");
  desc.add_options()
    ("pool", po::value<std::string>(&pool)->required(), "Pool name")
    ("logname", po::value<std::string>(&logname_req)->default_value(""), "Log name")
    ("width", po::value<int>(&width)->required(), "Width")
    ("qdepth", po::value<int>(&qdepth)->default_value(1), "Queue depth")
    ("iosize", po::value<int>(&iosize)->default_value(0), "IO Size")
  ;

  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);

  assert(qdepth > 0);

  // connect to rados
  librados::Rados cluster;
  cluster.init(NULL);
  cluster.conf_read_file(NULL);
  cluster.conf_parse_env(NULL);
  int ret = cluster.connect();
  assert(ret == 0);

  // open pool i/o context
  librados::IoCtx ioctx;
  ret = cluster.ioctx_create(pool.c_str(), ioctx);
  assert(ret == 0);

  // build a log name
  std::string logname;
  {
    std::stringstream ss;
    if (logname_req.size())
      ss << logname_req;
    else {
      boost::uuids::uuid uuid = boost::uuids::random_generator()();
      ss << uuid;
    }
    ss << ".log";
    logname = ss.str();
  }

  // setup log instance
  zlog::Log log;
  FakeSeqrClient client(logname);
  ret = zlog::Log::OpenOrCreate(ioctx, logname, width, &client, log);
  client.Init(ioctx);
  assert(ret == 0);

  // this is just a little hack that forces the epoch to be refreshed in the
  // log instance. otherwise when we blast out a bunch of async requests they
  // all end up having old epochs.
  ceph::bufferlist bl;
  log.Append(bl);

  std::unique_lock<std::mutex> lock(io_lock);

  ios_completed = 0;
  outstanding_ios = 0;
  latency_ms = 0;

  char iobuf[iosize];

  auto start = std::chrono::steady_clock::now();
  for (;;) {
    uint64_t ios_completed_end = ios_completed;
    uint64_t latency_ms_end = latency_ms;
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> diff = std::chrono::duration_cast<
      std::chrono::duration<double>>(end - start);
    double secs = diff.count();
    if (secs > 5) {
      double ios_per_sec = (double)ios_completed_end / secs;
      double avg_ms_lat = (double)latency_ms_end / (double)ios_completed_end;
      std::cout << "iops: " << ios_per_sec <<
        " avg_lat_ms: " << avg_ms_lat << std::endl;
      ios_completed = 0;
      latency_ms = 0;
      start = std::chrono::steady_clock::now();
    }

    while (outstanding_ios < qdepth) {

      AioState *state = new AioState;

      for (unsigned i = 0; i < iosize; i++) {
        iobuf[i] = (char)rand();
      }

      state->append_bl.append(iobuf, iosize);
      state->log = &log;

      state->c = zlog::Log::aio_create_completion(state, handle_append_cb);
      assert(state->c);
      state->submitted = std::chrono::steady_clock::now();
      ret = log.AioAppend(state->c, state->append_bl, &state->position);
      assert(ret == 0);
      outstanding_ios++;
    }
    io_cond.wait(lock, [&]{ return outstanding_ios < qdepth; });
  }

  return 0;
}
<|endoftext|>


<commit_before>/*! @page formatdoc File formats in mlpack

@section formatintro Introduction

mlpack supports a wide variety of data and model formats for use in both its
command-line programs and in C++ programs using mlpack via the
mlpack::data::Load() function.  This tutorial discusses the formats that are
supported and how to use them.

@section formattypes Supported dataset types

Datasets in mlpack are represented internally as sparse or dense numeric
matrices (specifically, as \c arma::mat or \c arma::sp_mat or similar).  This
means that when datasets are loaded from file, they must be converted to a
suitable numeric representation.  Therefore, in general, datasets on disk should
contain only numeric features in order to be loaded successfully by mlpack.

The types of datasets that mlpack can load are roughly the same as the types of
matrices that Armadillo can load.  When datasets are loaded by mlpack, \b the
\b "file's type is detected using the file's extension".  mlpack supports the
following file types:

 - csv (comma-separated values), denoted by .csv or .txt
 - tsv (tab-separated values), denoted by .tsv, .csv, or .txt
 - ASCII (raw ASCII, with space-separated values), denoted by .txt
 - Armadillo ASCII (Armadillo's text format with a header), denoted by .txt
 - PGM, denoted by .pgm
 - PPM, denoted by .ppm
 - Armadillo binary, denoted by .bin
 - Raw binary, denoted by .bin \b "(note: this will be loaded as"
   \b "one-dimensional data, which is likely not what is desired.)"
 - HDF5, denoted by .hdf, .hdf5, .h5, or .he5 \b "(note: HDF5 must be enabled"
   \b "in the Armadillo configuration)"
 - ARFF, denoted by .arff \b "(note: this is not supported by all mlpack"
   \b "command-line programs"; see \ref formatinfo )

Datasets that are loaded by mlpack should be stored with \b "one row for "
\b "one point" and \b "one column for one dimension".  Therefore, a dataset with
three two-dimensional points \f$(0, 1)\f$, \f$(3, 1)\f$, and \f$(5, -5)\f$ would
be stored in a csv file as:

\code
0, 1
3, 1
5, -5
\endcode

As noted earlier, the format is automatically detected at load time.  Therefore,
a dataset can be loaded in many ways:

\code
$ mlpack_logistic_regression -t dataset.csv -v
[INFO ] Loading 'dataset.csv' as CSV data.  Size is 32 x 37749.
...

$ mlpack_logistic_regression -t dataset.txt -v
[INFO ] Loading 'dataset.txt' as raw ASCII formatted data.  Size is 32 x 37749.
...

$ mlpack_logistic_regression -t dataset.h5 -v
[INFO ] Loading 'dataset.h5' as HDF5 data.  Size is 32 x 37749.
...
\endcode

Similarly, the format to save to is detected by the extension of the given
filename.

@section formatcpp Loading simple matrices in C++

When C++ is being written, the mlpack::data::Load() and mlpack::data::Save()
functions are used to load and save datasets, respectively.  These functions
should be preferred over the built-in Armadillo \c .load() and \c .save()
functions.

Matrices in mlpack are column-major, meaning that each column should correspond
to a point in the dataset and each row should correspond to a dimension; for
more information, see \ref matrices .  This is at odds with how the data is
stored in files; therefore, a transposition is required during load and save.
The mlpack::data::Load() and mlpack::data::Save() functions do this
automatically (unless otherwise specified), which is why they are preferred over
the Armadillo functions.

To load a matrix from file, the call is straightforward.  After creating a
matrix object, the data can be loaded:

\code
arma::mat dataset; // The data will be loaded into this matrix.
mlpack::data::Load("dataset.csv", dataset);
\endcode

Saving matrices is equally straightforward.  The code below generates a random
matrix with 10 points in 3 dimensions and saves it to a file as HDF5.

\code
// 3 dimensions (rows), with 10 points (columns).
arma::mat dataset = arma::randu<arma::mat>(3, 10);
mlpack::data::Save("dataset.h5", dataset);

As with the command-line programs, the type of data to be loaded is
automatically detected from the filename extension.  For more details, see the
mlpack::data::Load() and mlpack::data::Save() documentation.

@section formatcat Categorical features and command line programs

In some situations it is useful to represent data not just as a numeric matrix
but also as categorical data (i.e. with numeric but unordered categories).  This
support is useful for, e.g., decision trees and other models that support
categorical features.

In some machine learning situations, such as, e.g., decision trees, categorical
data can be used.  Categorical data might look like this (in CSV format):

\code
0, 1, "true", 3
5, -2, "false", 5
2, 2, "true", 4
3, -1, "true", 3
4, 4, "not sure", 0
0, 7, "false", 6
\endcode

In the example above, the third dimension (which takes values "true", "false",
and "not sure") is categorical.  mlpack can load and work with this data, but
the strings must be mapped to numbers, because all dataset in mlpack are
represented by Armadillo matrix objects.

From the perspective of an mlpack command-line program, this support is
transparent; mlpack will attempt to load the data file, and if it detects
entries in the file that are not numeric, it will map them to numbers and then
print, for each dimension, the number of mappings.  For instance, if we run the
\c mlpack_hoeffding_tree program (which supports categorical data) on the
dataset above (stored as dataset.csv), we receive this output during loading:

\code
$ mlpack_hoeffding_tree -t dataset.csv -l dataset.labels.csv -v
[INFO ] Loading 'dataset.csv' as CSV data.  Size is 6 x 4.
[INFO ] 0 mappings in dimension 0.
[INFO ] 0 mappings in dimension 1.
[INFO ] 3 mappings in dimension 2.
[INFO ] 0 mappings in dimension 3.
...
\endcode

@section formatcatcpp Categorical features and C++

When writing C++, loading categorical data is slightly more tricky: the mappings
from strings to integers must be preserved.  This is the purpose of the
mlpack::data::DatasetInfo class, which stores these mappings and can be used and
load and save time to apply and de-apply the mappings.

When loading a dataset with categorical data, the overload of
mlpack::data::Load() that takes an mlpack::data::DatasetInfo object should be
used.  An example is below:

\code
arma::mat dataset; // Load into this matrix.
mlpack::data::DatasetInfo info; // Store information about dataset in this.

// Load the ARFF dataset.
mlpack::data::Load("dataset.arff", dataset, info);
\endcode

After this load completes, the \c info object will hold the information about
the mappings necessary to load the dataset.  It is possible to re-use the
\c DatasetInfo object to load another dataset with the same mappings.  This is
useful when, for instance, both a training and test set are being loaded, and it
is necessary that the mappings from strings to integers for categorical features
are identical.  An example is given below.

\code
arma::mat trainingData; // Load training data into this matrix.
mlpack::data::DatasetInfo info; // This will store the mappings.

// Load the training data, and create the mappings in the 'info' object.
mlpack::data::Load("training_data.arff", trainingData, info);

// Load the test data, but re-use the 'info' object with the already initialized
// mappings.  This means that the same mappings will be applied to the test set.
mlpack::data::Load("test_data.arff", trainingData, info);
\endcode

When saving data, pass the same DatasetInfo object it was loaded with in order
to unmap the categorical features correctly.  The example below demonstrates
this functionality: it loads the dataset, increments all non-categorical
features by 1, and then saves the dataset with the same DatasetInfo it was
loaded with.

\code
arma::mat dataset; // Load data into this matrix.
mlpack::data::DatasetInfo info; // This will store the mappings.

// Load the dataset.
mlpack::data::Load("dataset.tsv", dataset, info);

// Loop over all features, and add 1 to all non-categorical features.
for (size_t i = 0; i < info.Dimensionality(); ++i)
{
  // The Type() function returns whether or not the data is numeric or
  // categorical.
  if (info.Type(i) != mlpack::data::Datatype::categorical)
    dataset.row(i) += 1.0;
}

// Save the modified dataset using the same DatasetInfo.
mlpack::data::Save("dataset-new.tsv", dataset, info);
\endcode

There is more functionality to the DatasetInfo class; for more information, see
the mlpack::data::DatasetInfo documentation.

@section formatmodels Loading and saving models

Using \c boost::serialization, mlpack is able to load and save machine learning
models with ease.  These models can currently be saved in three formats:

 - binary (.bin); this is not human-readable, but it is small
 - text (.txt); this is sort of human-readable and relatively small
 - xml (.xml); this is human-readable but very verbose and large

The type of file to save is determined by the given file extension, as with the
other loading and saving functionality in mlpack.  Below is an example where a
dataset stored as TSV and labels stored as ASCII text are used to train a
logistic regression model, which is then saved to model.xml.

\code
$ mlpack_logistic_regression -t training_dataset.tsv -l training_labels.txt \
> -M model.xml
\endcode

Many mlpack command-line programs have support for loading and saving models
through the \c --input_model_file (\c -m) and \c --output_model_file (\c -M)
options; for more information, see the documentation for each program
(accessible by passing \c --help as a parameter).

@section formatmodels Loading and saving models in C++

mlpack uses the \c boost::serialization library internally to perform loading
and saving of models, and provides convenience overloads of mlpack::data::Load()
and mlpack::data::Save() to load and save these models.

To be serializable, a class must implement the method

\code
template<typename Archive>
void Serialize(Archive& ar, const unsigned int version);
\endcode

\note
For more information on this method and how it works, see the TODO: add link
boost::serialization documentation.  Note that mlpack uses a \c Serialize()
method and not a \c serialize() method, and also mlpack uses the
mlpack::data::CreateNVP() method instead of \c BOOST_SERIALIZATION_NVP() ; this
is for coherence with the mlpack style guidelines, and is done via a
particularly complex bit of template metaprogramming in
src/mlpack/core/data/serialization_shim.hpp (read that file if you want your
head to hurt!).

\note
Examples of Serialize() methods can be found in most classes; one fairly
straightforward example is the mlpack::math::Range class (TODO: add link).  A
more complex example is the mlpack::tree::BinarySpaceTree class (TODO: add
link).

Using the mlpack::data::Load() and mlpack::data::Save() classes is easy if the
type being saved has a \c Serialize() method implemented: simply call either
function with a filename, a name for the object to save, and the object itself.
The example below, for instance, creates an mlpack::math::Range object and saves
it as range.txt.  Then, that range is loaded from file into another
mlpack::math::Range object.

\code
// Create range and save it.
mlpack::math::Range r(0.0, 5.0);
mlpack::data::Save("range.txt", "range", r);

// Load into new range.
mlpack::math::Range newRange;
mlpack::data::Load("range.txt", "range", newRange);
\endcode

It is important to be sure that you load the appropriate type; if you save, for
instance, an mlpack::regression::LogisticRegression object and attempt to load
it as an mlpack::math::Range object, the load will fail and an exception will be
thrown.  (When the object is saved as binary (.bin), it is possible that the
load will not fail, but instead load with mangled data, which is perhaps even
worse!)

@section formatfinal Final notes

If the examples here are unclear, it would be worth looking into the ways that
mlpack::data::Load() and mlpack::data::Save() are used in the code.  Some
example files that may be useful to this end:

 - src/mlpack/methods/logistic_regression/logistic_regression_main.cpp
 - src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp
 - src/mlpack/methods/neighbor_search/allknn_main.cpp

If you are interested in adding support for more data types to mlpack, it would
be preferable to add the support upstream to Armadillo instead, so that may be a
better direction to go first.  Then very little code modification for mlpack
will be necessary.

*/
<commit_msg>Update documentation.<commit_after>/*! @page formatdoc File formats in mlpack

@section formatintro Introduction

mlpack supports a wide variety of data and model formats for use in both its
command-line programs and in C++ programs using mlpack via the
mlpack::data::Load() function.  This tutorial discusses the formats that are
supported and how to use them.

@section formattypes Supported dataset types

Datasets in mlpack are represented internally as sparse or dense numeric
matrices (specifically, as \c arma::mat or \c arma::sp_mat or similar).  This
means that when datasets are loaded from file, they must be converted to a
suitable numeric representation.  Therefore, in general, datasets on disk should
contain only numeric features in order to be loaded successfully by mlpack.

The types of datasets that mlpack can load are roughly the same as the types of
matrices that Armadillo can load.  When datasets are loaded by mlpack, \b the
\b "file's type is detected using the file's extension".  mlpack supports the
following file types:

 - csv (comma-separated values), denoted by .csv or .txt
 - tsv (tab-separated values), denoted by .tsv, .csv, or .txt
 - ASCII (raw ASCII, with space-separated values), denoted by .txt
 - Armadillo ASCII (Armadillo's text format with a header), denoted by .txt
 - PGM, denoted by .pgm
 - PPM, denoted by .ppm
 - Armadillo binary, denoted by .bin
 - Raw binary, denoted by .bin \b "(note: this will be loaded as"
   \b "one-dimensional data, which is likely not what is desired.)"
 - HDF5, denoted by .hdf, .hdf5, .h5, or .he5 (note: HDF5 must be enabled"
   in the Armadillo configuration)
 - ARFF, denoted by .arff (note: this is not supported by all mlpack"
   command-line programs ; see \ref formatcat )

Datasets that are loaded by mlpack should be stored with one row for
one point and one column for one dimension.  Therefore, a dataset
with three two-dimensional points \f$(0, 1)\f$, \f$(3, 1)\f$, and \f$(5, -5)\f$
would be stored in a csv file as:

\code
0, 1
3, 1
5, -5
\endcode

As noted earlier, the format is automatically detected at load time.  Therefore,
a dataset can be loaded in many ways:

\code
$ mlpack_logistic_regression -t dataset.csv -v
[INFO ] Loading 'dataset.csv' as CSV data.  Size is 32 x 37749.
...

$ mlpack_logistic_regression -t dataset.txt -v
[INFO ] Loading 'dataset.txt' as raw ASCII formatted data.  Size is 32 x 37749.
...

$ mlpack_logistic_regression -t dataset.h5 -v
[INFO ] Loading 'dataset.h5' as HDF5 data.  Size is 32 x 37749.
...
\endcode

Similarly, the format to save to is detected by the extension of the given
filename.

@section formatcpp Loading simple matrices in C++

When C++ is being written, the mlpack::data::Load() and mlpack::data::Save()
functions are used to load and save datasets, respectively.  These functions
should be preferred over the built-in Armadillo \c .load() and \c .save()
functions.

Matrices in mlpack are column-major, meaning that each column should correspond
to a point in the dataset and each row should correspond to a dimension; for
more information, see \ref matrices .  This is at odds with how the data is
stored in files; therefore, a transposition is required during load and save.
The mlpack::data::Load() and mlpack::data::Save() functions do this
automatically (unless otherwise specified), which is why they are preferred over
the Armadillo functions.

To load a matrix from file, the call is straightforward.  After creating a
matrix object, the data can be loaded:

\code
arma::mat dataset; // The data will be loaded into this matrix.
mlpack::data::Load("dataset.csv", dataset);
\endcode

Saving matrices is equally straightforward.  The code below generates a random
matrix with 10 points in 3 dimensions and saves it to a file as HDF5.

\code
// 3 dimensions (rows), with 10 points (columns).
arma::mat dataset = arma::randu<arma::mat>(3, 10);
mlpack::data::Save("dataset.h5", dataset);

As with the command-line programs, the type of data to be loaded is
automatically detected from the filename extension.  For more details, see the
mlpack::data::Load() and mlpack::data::Save() documentation.

@section formatcat Categorical features and command line programs

In some situations it is useful to represent data not just as a numeric matrix
but also as categorical data (i.e. with numeric but unordered categories).  This
support is useful for, e.g., decision trees and other models that support
categorical features.

In some machine learning situations, such as, e.g., decision trees, categorical
data can be used.  Categorical data might look like this (in CSV format):

\code
0, 1, "true", 3
5, -2, "false", 5
2, 2, "true", 4
3, -1, "true", 3
4, 4, "not sure", 0
0, 7, "false", 6
\endcode

In the example above, the third dimension (which takes values "true", "false",
and "not sure") is categorical.  mlpack can load and work with this data, but
the strings must be mapped to numbers, because all dataset in mlpack are
represented by Armadillo matrix objects.

From the perspective of an mlpack command-line program, this support is
transparent; mlpack will attempt to load the data file, and if it detects
entries in the file that are not numeric, it will map them to numbers and then
print, for each dimension, the number of mappings.  For instance, if we run the
\c mlpack_hoeffding_tree program (which supports categorical data) on the
dataset above (stored as dataset.csv), we receive this output during loading:

\code
$ mlpack_hoeffding_tree -t dataset.csv -l dataset.labels.csv -v
[INFO ] Loading 'dataset.csv' as CSV data.  Size is 6 x 4.
[INFO ] 0 mappings in dimension 0.
[INFO ] 0 mappings in dimension 1.
[INFO ] 3 mappings in dimension 2.
[INFO ] 0 mappings in dimension 3.
...
\endcode

Currently, only the \c mlpack_hoeffding_tree program supports loading
categorical data, and this is also the only program that supports loading an
ARFF dataset.

@section formatcatcpp Categorical features and C++

When writing C++, loading categorical data is slightly more tricky: the mappings
from strings to integers must be preserved.  This is the purpose of the
mlpack::data::DatasetInfo class, which stores these mappings and can be used and
load and save time to apply and de-apply the mappings.

When loading a dataset with categorical data, the overload of
mlpack::data::Load() that takes an mlpack::data::DatasetInfo object should be
used.  An example is below:

\code
arma::mat dataset; // Load into this matrix.
mlpack::data::DatasetInfo info; // Store information about dataset in this.

// Load the ARFF dataset.
mlpack::data::Load("dataset.arff", dataset, info);
\endcode

After this load completes, the \c info object will hold the information about
the mappings necessary to load the dataset.  It is possible to re-use the
\c DatasetInfo object to load another dataset with the same mappings.  This is
useful when, for instance, both a training and test set are being loaded, and it
is necessary that the mappings from strings to integers for categorical features
are identical.  An example is given below.

\code
arma::mat trainingData; // Load training data into this matrix.
mlpack::data::DatasetInfo info; // This will store the mappings.

// Load the training data, and create the mappings in the 'info' object.
mlpack::data::Load("training_data.arff", trainingData, info);

// Load the test data, but re-use the 'info' object with the already initialized
// mappings.  This means that the same mappings will be applied to the test set.
mlpack::data::Load("test_data.arff", trainingData, info);
\endcode

When saving data, pass the same DatasetInfo object it was loaded with in order
to unmap the categorical features correctly.  The example below demonstrates
this functionality: it loads the dataset, increments all non-categorical
features by 1, and then saves the dataset with the same DatasetInfo it was
loaded with.

\code
arma::mat dataset; // Load data into this matrix.
mlpack::data::DatasetInfo info; // This will store the mappings.

// Load the dataset.
mlpack::data::Load("dataset.tsv", dataset, info);

// Loop over all features, and add 1 to all non-categorical features.
for (size_t i = 0; i < info.Dimensionality(); ++i)
{
  // The Type() function returns whether or not the data is numeric or
  // categorical.
  if (info.Type(i) != mlpack::data::Datatype::categorical)
    dataset.row(i) += 1.0;
}

// Save the modified dataset using the same DatasetInfo.
mlpack::data::Save("dataset-new.tsv", dataset, info);
\endcode

There is more functionality to the DatasetInfo class; for more information, see
the mlpack::data::DatasetInfo documentation.

@section formatmodels Loading and saving models

Using \c boost::serialization, mlpack is able to load and save machine learning
models with ease.  These models can currently be saved in three formats:

 - binary (.bin); this is not human-readable, but it is small
 - text (.txt); this is sort of human-readable and relatively small
 - xml (.xml); this is human-readable but very verbose and large

The type of file to save is determined by the given file extension, as with the
other loading and saving functionality in mlpack.  Below is an example where a
dataset stored as TSV and labels stored as ASCII text are used to train a
logistic regression model, which is then saved to model.xml.

\code
$ mlpack_logistic_regression -t training_dataset.tsv -l training_labels.txt \
> -M model.xml
\endcode

Many mlpack command-line programs have support for loading and saving models
through the \c --input_model_file (\c -m) and \c --output_model_file (\c -M)
options; for more information, see the documentation for each program
(accessible by passing \c --help as a parameter).

@section formatmodels Loading and saving models in C++

mlpack uses the \c boost::serialization library internally to perform loading
and saving of models, and provides convenience overloads of mlpack::data::Load()
and mlpack::data::Save() to load and save these models.

To be serializable, a class must implement the method

\code
template<typename Archive>
void Serialize(Archive& ar, const unsigned int version);
\endcode

\note
For more information on this method and how it works, see the TODO: add link
boost::serialization documentation.  Note that mlpack uses a \c Serialize()
method and not a \c serialize() method, and also mlpack uses the
mlpack::data::CreateNVP() method instead of \c BOOST_SERIALIZATION_NVP() ; this
is for coherence with the mlpack style guidelines, and is done via a
particularly complex bit of template metaprogramming in
src/mlpack/core/data/serialization_shim.hpp (read that file if you want your
head to hurt!).

\note
Examples of Serialize() methods can be found in most classes; one fairly
straightforward example is the mlpack::math::Range class (TODO: add link).  A
more complex example is the mlpack::tree::BinarySpaceTree class (TODO: add
link).

Using the mlpack::data::Load() and mlpack::data::Save() classes is easy if the
type being saved has a \c Serialize() method implemented: simply call either
function with a filename, a name for the object to save, and the object itself.
The example below, for instance, creates an mlpack::math::Range object and saves
it as range.txt.  Then, that range is loaded from file into another
mlpack::math::Range object.

\code
// Create range and save it.
mlpack::math::Range r(0.0, 5.0);
mlpack::data::Save("range.txt", "range", r);

// Load into new range.
mlpack::math::Range newRange;
mlpack::data::Load("range.txt", "range", newRange);
\endcode

It is important to be sure that you load the appropriate type; if you save, for
instance, an mlpack::regression::LogisticRegression object and attempt to load
it as an mlpack::math::Range object, the load will fail and an exception will be
thrown.  (When the object is saved as binary (.bin), it is possible that the
load will not fail, but instead load with mangled data, which is perhaps even
worse!)

@section formatfinal Final notes

If the examples here are unclear, it would be worth looking into the ways that
mlpack::data::Load() and mlpack::data::Save() are used in the code.  Some
example files that may be useful to this end:

 - src/mlpack/methods/logistic_regression/logistic_regression_main.cpp
 - src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp
 - src/mlpack/methods/neighbor_search/allknn_main.cpp

If you are interested in adding support for more data types to mlpack, it would
be preferable to add the support upstream to Armadillo instead, so that may be a
better direction to go first.  Then very little code modification for mlpack
will be necessary.

*/
<|endoftext|>


<commit_before>#include "lib_sound.h"
#include <inttypes.h>

#define BUFFER_SAMPLE_COUNT (2048)
#define FREQUENCY (11025)
#include <memory.h>

#ifdef WIN32

#include <stdlib.h>
#include <stdio.h>

#include <al.h>
#include <alc.h>

///// sound stuff - source: https://stackoverflow.com/questions/5469030/c-play-back-a-tone-generated-from-a-sinusoidal-wave
#define CASE_RETURN(err) case (err): return "##err"
const char* al_err_str(ALenum err) {
    switch(err) {
        CASE_RETURN(AL_NO_ERROR);
        CASE_RETURN(AL_INVALID_NAME);
        CASE_RETURN(AL_INVALID_ENUM);
        CASE_RETURN(AL_INVALID_VALUE);
        CASE_RETURN(AL_INVALID_OPERATION);
        CASE_RETURN(AL_OUT_OF_MEMORY);
    }
    return "unknown";
}
#undef CASE_RETURN
#define __al_check_error(file,line) \
    do { \
        ALenum err = alGetError(); \
        for(; err!=AL_NO_ERROR; err=alGetError()) { \
            printf("AL Error %s at %s:%d\n",al_err_str(err),file,":",line); \
        } \
    }while(0)

#define al_check_error() \
    __al_check_error(__FILE__, __LINE__)

ALuint src = 0;
static ALuint buf[2];
static uint8_t sampleBuffer[BUFFER_SAMPLE_COUNT/2];

void init_al() {
    ALCdevice *dev = NULL;
    ALCcontext *ctx = NULL;

    const char *defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
    printf("Default device: %s\n", defname);

    dev = alcOpenDevice(defname);
    ctx = alcCreateContext(dev, NULL);
    alcMakeContextCurrent(ctx);

    alGenBuffers(2, buf);
    al_check_error();
    alGenSources(1, &src);
    memset(sampleBuffer,0,sizeof(sampleBuffer));
    for (int i = 0; i < 2; i++)
    {
       alBufferData(buf[i], AL_FORMAT_MONO8, sampleBuffer, sizeof(sampleBuffer), FREQUENCY);
       alSourceQueueBuffers(src, 1, &buf[i]);
    }
    alSourcePlay(src);
}

void update_al(int8_t* buffer, uint16_t* bufferPos, uint16_t* playbackPos) {
    int pcnt;
    alGetSourcei(src, AL_BUFFERS_PROCESSED, &pcnt);
    for (int i=0;i<pcnt;i+=1) {
        ALuint buf;
        alSourceUnqueueBuffers(src, 1, &buf);
        int remaining = sizeof(sampleBuffer);
        int p = 0;
        memset(sampleBuffer,0,sizeof(sampleBuffer));
        //printf("%d %d\n",*playbackPos, *bufferPos);
        while (*playbackPos != *bufferPos && p < sizeof(sampleBuffer)) {
            int8_t s = buffer[*playbackPos];
            sampleBuffer[p++] = (uint8_t)(s + 127);
            buffer[*playbackPos] = 0;
            //printf(" %d",s);
            *playbackPos = (*playbackPos + 1) % BUFFER_SAMPLE_COUNT;
        }
        //printf("\n");

        alBufferData(buf, AL_FORMAT_MONO8, sampleBuffer, sizeof(sampleBuffer), FREQUENCY);
        alSourceQueueBuffers(src, 1, &buf);

    }
    ALenum state;
    alGetSourcei(src, AL_SOURCE_STATE, &state);
    if (state != AL_PLAYING)
        alSourcePlay(src);
}

#define NATIVE_INIT init_al
#define NATIVE_UPDATE update_al

#else // arduino init

#include <Wire.h>
#include <SPI.h>

#ifdef __cplusplus
extern "C" {
#endif
int8_t *audioBuffer;
int16_t *audioBufferPos;

void TC5_Handler (void)
{

  int v = audioBuffer ? audioBuffer[audioBufferPos] + 127 : 127;
  if (audioBuffer) {
      audioBufferPos+=1;
      if( *audioBufferPos > BUFFER_SAMPLE_COUNT )
        *audioBufferPos = 0;
  }

  while( DAC->STATUS.bit.SYNCBUSY == 1 );
  DAC->DATA.reg = v<<2;
  while( DAC->STATUS.bit.SYNCBUSY == 1 );

  // Clear the interrupt
  TC5->COUNT16.INTFLAG.bit.MC0 = 1;
}
#ifdef __cplusplus
}
#endif

bool tcIsSyncing()
{
  return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
}

void tcStart()
{
  // Enable TC
  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
  while( tcIsSyncing());
}

void tcReset()
{
  // Reset TCx
  TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
  while( tcIsSyncing());
  while( TC5->COUNT16.CTRLA.bit.SWRST );
}

void tcStop()
{
  // Disable TC5
  TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
  while( tcIsSyncing());
}

void tcConfigure( uint32_t sampleRate )
{
  // Enable GCLK for TCC2 and TC5 (timer counter input clock)
  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)) ;
  while (GCLK->STATUS.bit.SYNCBUSY);

  tcReset();

  // Set Timer counter Mode to 16 bits
  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;

  // Set TC5 mode as match frequency
  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;

  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1;

  TC5->COUNT16.CC[0].reg = (uint16_t) (SystemCoreClock / sampleRate - 1);
  while (tcIsSyncing());

  // Configure interrupt request
  NVIC_DisableIRQ(TC5_IRQn);
  NVIC_ClearPendingIRQ(TC5_IRQn);
  NVIC_SetPriority(TC5_IRQn, 0);
  NVIC_EnableIRQ(TC5_IRQn);

  // Enable the TC5 interrupt request
  TC5->COUNT16.INTENSET.bit.MC0 = 1;
  while( tcIsSyncing());
}

void update_tc(int8_t* buffer, uint16_t* bufferPos, uint16_t* playbackPos) {
    audioBuffer = buffer;
    audioBufferPos = playbackPos;
}

void init_tc() {
    tcConfigure( FREQUENCY );
    tcStart();
}

#define NATIVE_INIT init_tc
#define NATIVE_UPDATE update_tc

#endif // WIN32

#define MAX_SAMPLE_PLAYBACKS 8

namespace Sound {
    void SamplePlayback::stop() {
        samples = 0;
    }
    void SamplePlayback::init(const int8_t *s, uint16_t len, uint16_t speed, uint16_t v, uint16_t loops, uint16_t id) {
        samples = s;
        length = len;
        pos = 0;
        this->speed = speed;
        this->id = id;
        loopCount = loops;
        volume = v;
        interpolate = 0;
        changeInterval = 0;
    }
    void SamplePlayback::fillBuffer(int8_t *buf, uint16_t n) {
        if (!samples) return;
        uint32_t len = length << 8;
        for (int i=0;i<n;i+=1) {
            uint16_t p = pos >> 8;
            int32_t a = samples[p];
            int32_t res;
            if (interpolate) {
                int32_t b = samples[p + 1 < length ? p + 1 : 0];
                uint8_t mix = pos & 0xff;
                res = (a * (0xff-mix) + b * mix) >> 8;

            } else {
                res = a;
            }
            //printf("%d ",res);
            int32_t s = res + buf[n];
            //printf("%d %d\n",samples[pos>>8],pos>>8);
            //pos += speed;
            s = (s * volume) >> 8;
            if (s < -126) s = -126;
            else if (s > 127) s = 127;
            buf[i] = (int8_t) s;
            pos += speed;
            if (changeInterval) {
                changeTimer += speed;
                while (speed > 0 && changeTimer >= changeInterval) {
                    changeTimer -= changeInterval;
                    if (speed >= -changeSpeed) speed += changeSpeed;
                    else speed = 0;
                    if (volume >= -changeVolume) volume += changeVolume;
                    else volume = 0;
                }
                if (speed == 0 || volume == 0) {
                    stop();
                    return;
                }
            }
            while (pos >= (len)) {
                if (loopCount <= 1) {
                    stop();
                    return;
                }
                if (loopCount < 0xffff) loopCount -= 1;
                pos -= len;
            }
        }
    }

    int8_t sampleBuffer[BUFFER_SAMPLE_COUNT];
    uint16_t playbackPosition; // position that is played / queued on sound out
    uint16_t bufferPosition; // position we have filled the buffer to
    static SamplePlayback playbacks[MAX_SAMPLE_PLAYBACKS];

    void fillBuffer(uint16_t from, uint16_t n) {
        //printf("%d %d %d %d\n",from,n,playbackPosition, bufferPosition);
        memset(&sampleBuffer[from],0,n);
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1) {
            playbacks[i].fillBuffer(&sampleBuffer[from], n);
        }
        //for (int i=from;i<from+n;i+=1)
        //    sampleBuffer[i] = i % (i%128 + 1) % 16-8;
    }

    SamplePlayback* SamplePlayback::enableInterpolate(){
        interpolate = 1;
        return this;
    }
    SamplePlayback* SamplePlayback::setChange(uint32_t interval, int16_t vol, int16_t speed){
        changeSpeed = speed;
        changeVolume = vol;
        changeInterval = interval;
        return this;
    }

    void fillBuffer() {
        playbackPosition%=BUFFER_SAMPLE_COUNT;
        uint16_t pos = playbackPosition;
        if (pos <= bufferPosition) {
            if (bufferPosition < BUFFER_SAMPLE_COUNT) {
                fillBuffer(bufferPosition,BUFFER_SAMPLE_COUNT - bufferPosition);
            }
            if (pos > 1) {
                fillBuffer(0, pos - 1);
                bufferPosition = pos - 1;
            } else {
                bufferPosition = BUFFER_SAMPLE_COUNT;
            }
        } else {
            if (pos - bufferPosition > 1) {
                fillBuffer(bufferPosition, pos-bufferPosition-1);
                bufferPosition = pos-1;
            }
        }

    }
    void init() {
        NATIVE_INIT();
        fillBuffer();
        //static const int8_t samples[] = {100,-100};//,80,-80,50,-50,10,-10};
        //playSample(1,samples, sizeof(samples), 0x20,0x200,0xff);
    }
    SamplePlayback* playSample(uint16_t id, const int8_t *samples, uint16_t length, uint16_t speed, uint16_t volume, uint16_t loops) {
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1)
        {
            if (playbacks[i].samples == 0) {
                playbacks[i].init(samples, length, speed, volume, loops,id);
                return &playbacks[i];
            }
        }
        static SamplePlayback def;
        return &def;
    }
    void stopSample(uint16_t id) {
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1)
        {
            if (playbacks[i].id == id) {
                playbacks[i].stop();
            }
        }
    }
    void updateVolume(int16_t id, uint16_t volume) {
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1)
        {
            if (playbacks[i].id == id) {
                playbacks[i].volume = volume;
            }
        }
    }
    void updateSpeed(int16_t id, uint16_t speed) {
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1)
        {
            if (playbacks[i].id == id) {
                playbacks[i].speed = speed;
            }
        }
    }
    void tick() {
        fillBuffer();
        NATIVE_UPDATE(sampleBuffer, &bufferPosition, &playbackPosition);
    }

}
<commit_msg>Sound output working on the Arcade as well<commit_after>#include "lib_sound.h"
#include <inttypes.h>

#define BUFFER_SAMPLE_COUNT (1024)
#define FREQUENCY (11025)

#include <string.h>

#ifdef WIN32

#include <stdlib.h>
#include <stdio.h>

#include <al.h>
#include <alc.h>

///// sound stuff - source: https://stackoverflow.com/questions/5469030/c-play-back-a-tone-generated-from-a-sinusoidal-wave
#define CASE_RETURN(err) case (err): return "##err"
const char* al_err_str(ALenum err) {
    switch(err) {
        CASE_RETURN(AL_NO_ERROR);
        CASE_RETURN(AL_INVALID_NAME);
        CASE_RETURN(AL_INVALID_ENUM);
        CASE_RETURN(AL_INVALID_VALUE);
        CASE_RETURN(AL_INVALID_OPERATION);
        CASE_RETURN(AL_OUT_OF_MEMORY);
    }
    return "unknown";
}
#undef CASE_RETURN
#define __al_check_error(file,line) \
    do { \
        ALenum err = alGetError(); \
        for(; err!=AL_NO_ERROR; err=alGetError()) { \
            printf("AL Error %s at %s:%d\n",al_err_str(err),file,":",line); \
        } \
    }while(0)

#define al_check_error() \
    __al_check_error(__FILE__, __LINE__)

ALuint src = 0;
static ALuint buf[2];
static uint8_t sampleBuffer[BUFFER_SAMPLE_COUNT/2];

void init_al() {
    ALCdevice *dev = NULL;
    ALCcontext *ctx = NULL;

    const char *defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
    printf("Default device: %s\n", defname);

    dev = alcOpenDevice(defname);
    ctx = alcCreateContext(dev, NULL);
    alcMakeContextCurrent(ctx);

    alGenBuffers(2, buf);
    al_check_error();
    alGenSources(1, &src);
    memset(sampleBuffer,0,sizeof(sampleBuffer));
    for (int i = 0; i < 2; i++)
    {
       alBufferData(buf[i], AL_FORMAT_MONO8, sampleBuffer, sizeof(sampleBuffer), FREQUENCY);
       alSourceQueueBuffers(src, 1, &buf[i]);
    }
    alSourcePlay(src);
}

void update_al(int8_t* buffer, uint16_t* bufferPos, uint16_t* playbackPos) {
    int pcnt;
    alGetSourcei(src, AL_BUFFERS_PROCESSED, &pcnt);
    for (int i=0;i<pcnt;i+=1) {
        ALuint buf;
        alSourceUnqueueBuffers(src, 1, &buf);
        int remaining = sizeof(sampleBuffer);
        int p = 0;
        memset(sampleBuffer,0,sizeof(sampleBuffer));
        while (*playbackPos != *bufferPos && p < sizeof(sampleBuffer)) {
            int8_t s = buffer[*playbackPos];
            sampleBuffer[p++] = (uint8_t)(s + 127);
            buffer[*playbackPos] = 0;
            //printf(" %d",s);
            *playbackPos = (*playbackPos + 1) % BUFFER_SAMPLE_COUNT;
        }
        //printf("\n");

        alBufferData(buf, AL_FORMAT_MONO8, sampleBuffer, sizeof(sampleBuffer), FREQUENCY);
        alSourceQueueBuffers(src, 1, &buf);

    }
    ALenum state;
    alGetSourcei(src, AL_SOURCE_STATE, &state);
    if (state != AL_PLAYING)
        alSourcePlay(src);
}

#define NATIVE_INIT init_al
#define NATIVE_UPDATE update_al

#else // arduino init

#include <Wire.h>
#include <SPI.h>

#ifdef __cplusplus
extern "C" {
#endif
int8_t *audioBuffer;
uint16_t *audioBufferPos;

void TC5_Handler (void)
{

  int v = audioBuffer ? audioBuffer[*audioBufferPos] + 127 : 127;
  if (audioBuffer) {
      *audioBufferPos+=1;
      if( *audioBufferPos > BUFFER_SAMPLE_COUNT )
        *audioBufferPos = 0;
  }

  while( DAC->STATUS.bit.SYNCBUSY == 1 );
  DAC->DATA.reg = v<<2;
  while( DAC->STATUS.bit.SYNCBUSY == 1 );

  // Clear the interrupt
  TC5->COUNT16.INTFLAG.bit.MC0 = 1;
}
#ifdef __cplusplus
}
#endif

bool tcIsSyncing()
{
  return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
}

void tcStart()
{
  // Enable TC
  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
  while( tcIsSyncing());
}

void tcReset()
{
  // Reset TCx
  TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
  while( tcIsSyncing());
  while( TC5->COUNT16.CTRLA.bit.SWRST );
}

void tcStop()
{
  // Disable TC5
  TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
  while( tcIsSyncing());
}

void tcConfigure( uint32_t sampleRate )
{
  // Enable GCLK for TCC2 and TC5 (timer counter input clock)
  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)) ;
  while (GCLK->STATUS.bit.SYNCBUSY);

  tcReset();

  // Set Timer counter Mode to 16 bits
  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;

  // Set TC5 mode as match frequency
  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;

  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1;

  TC5->COUNT16.CC[0].reg = (uint16_t) (SystemCoreClock / sampleRate - 1);
  while (tcIsSyncing());

  // Configure interrupt request
  NVIC_DisableIRQ(TC5_IRQn);
  NVIC_ClearPendingIRQ(TC5_IRQn);
  NVIC_SetPriority(TC5_IRQn, 0);
  NVIC_EnableIRQ(TC5_IRQn);

  // Enable the TC5 interrupt request
  TC5->COUNT16.INTENSET.bit.MC0 = 1;
  while( tcIsSyncing());
}

void update_tc(int8_t* buffer, uint16_t* bufferPos, uint16_t* playbackPos) {
    audioBuffer = buffer;
    audioBufferPos = playbackPos;
}

void init_tc() {
    tcConfigure( FREQUENCY );
    tcStart();
}

#define NATIVE_INIT init_tc
#define NATIVE_UPDATE update_tc

#endif // WIN32

#define MAX_SAMPLE_PLAYBACKS 8

namespace Sound {
    void SamplePlayback::stop() {
        samples = 0;
    }
    void SamplePlayback::init(const int8_t *s, uint16_t len, uint16_t speed, uint16_t v, uint16_t loops, uint16_t id) {
        samples = s;
        length = len;
        pos = 0;
        this->speed = speed;
        this->id = id;
        loopCount = loops;
        volume = v;
        interpolate = 0;
        changeInterval = 0;
    }
    void SamplePlayback::fillBuffer(int8_t *buf, uint16_t n) {
        if (!samples) return;
        uint32_t len = length << 8;
        for (int i=0;i<n;i+=1) {
            uint16_t p = pos >> 8;
            int32_t a = samples[p];
            int32_t res;
            if (interpolate) {
                int32_t b = samples[p + 1 < length ? p + 1 : 0];
                uint8_t mix = pos & 0xff;
                res = (a * (0xff-mix) + b * mix) >> 8;

            } else {
                res = a;
            }
            //printf("%d ",res);
            int32_t s = res + buf[n];
            //printf("%d %d\n",samples[pos>>8],pos>>8);
            //pos += speed;
            s = (s * volume) >> 8;
            if (s < -126) s = -126;
            else if (s > 127) s = 127;
            buf[i] = (int8_t) s;
            pos += speed;
            if (changeInterval) {
                changeTimer += speed;
                while (speed > 0 && changeTimer >= changeInterval) {
                    changeTimer -= changeInterval;
                    if (speed >= -changeSpeed) speed += changeSpeed;
                    else speed = 0;
                    if (volume >= -changeVolume) volume += changeVolume;
                    else volume = 0;
                }
                if (speed == 0 || volume == 0) {
                    stop();
                    return;
                }
            }
            while (pos >= (len)) {
                if (loopCount <= 1) {
                    stop();
                    return;
                }
                if (loopCount < 0xffff) loopCount -= 1;
                pos -= len;
            }
        }
    }

    int8_t sampleBuffer[BUFFER_SAMPLE_COUNT];
    uint16_t playbackPosition; // position that is played / queued on sound out
    uint16_t bufferPosition; // position we have filled the buffer to
    static SamplePlayback playbacks[MAX_SAMPLE_PLAYBACKS];

    void fillBuffer(uint16_t from, uint16_t n) {
        //printf("%d %d %d %d\n",from,n,playbackPosition, bufferPosition);
        memset(&sampleBuffer[from],0,n);
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1) {
            playbacks[i].fillBuffer(&sampleBuffer[from], n);
        }
        //for (int i=from;i<from+n;i+=1)
        //    sampleBuffer[i] = i % (i%128 + 1) % 16-8;
    }

    SamplePlayback* SamplePlayback::enableInterpolate(){
        interpolate = 1;
        return this;
    }
    SamplePlayback* SamplePlayback::setChange(uint32_t interval, int16_t vol, int16_t speed){
        changeSpeed = speed;
        changeVolume = vol;
        changeInterval = interval;
        return this;
    }

    void fillBuffer() {
        playbackPosition%=BUFFER_SAMPLE_COUNT;
        uint16_t pos = playbackPosition;
        if (pos <= bufferPosition) {
            if (bufferPosition < BUFFER_SAMPLE_COUNT) {
                fillBuffer(bufferPosition,BUFFER_SAMPLE_COUNT - bufferPosition);
            }
            if (pos > 1) {
                fillBuffer(0, pos - 1);
                bufferPosition = pos - 1;
            } else {
                bufferPosition = BUFFER_SAMPLE_COUNT;
            }
        } else {
            if (pos - bufferPosition > 1) {
                fillBuffer(bufferPosition, pos-bufferPosition-1);
                bufferPosition = pos-1;
            }
        }

    }
    void init() {
        NATIVE_INIT();
        fillBuffer();
        //static const int8_t samples[] = {100,-100};//,80,-80,50,-50,10,-10};
        //playSample(1,samples, sizeof(samples), 0x20,0x200,0xff);
    }
    SamplePlayback* playSample(uint16_t id, const int8_t *samples, uint16_t length, uint16_t speed, uint16_t volume, uint16_t loops) {
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1)
        {
            if (playbacks[i].samples == 0) {
                playbacks[i].init(samples, length, speed, volume, loops,id);
                return &playbacks[i];
            }
        }
        static SamplePlayback def;
        return &def;
    }
    void stopSample(uint16_t id) {
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1)
        {
            if (playbacks[i].id == id) {
                playbacks[i].stop();
            }
        }
    }
    void updateVolume(int16_t id, uint16_t volume) {
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1)
        {
            if (playbacks[i].id == id) {
                playbacks[i].volume = volume;
            }
        }
    }
    void updateSpeed(int16_t id, uint16_t speed) {
        for (int i=0;i<MAX_SAMPLE_PLAYBACKS;i+=1)
        {
            if (playbacks[i].id == id) {
                playbacks[i].speed = speed;
            }
        }
    }
    void tick() {
        fillBuffer();
        NATIVE_UPDATE(sampleBuffer, &bufferPosition, &playbackPosition);
    }

}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Intel Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "xwalk/application/tools/tizen/xwalk_package_installer_helper.h"

#include <assert.h>
#include <stdio.h>
#include <pkgmgr/pkgmgr_parser.h>

#undef LOG
#include <string>
#include "base/files/file_path.h"
#include "base/file_util.h"

namespace {

typedef int (*PkgParser)(const char*, char* const*);

const base::FilePath kIconDir("/opt/share/icons/default/small/");
const base::FilePath kXmlDir("/opt/share/packages/");
const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
const std::string kServicePrefix("xwalk-service.");

// Package type sent in every signal.
const char PKGMGR_PKG_TYPE[] = "rpm";

// Notification about operation start.
const char PKGMGR_START_KEY[] = "start";

// Value for new installation.
const char PKGMGR_START_INSTALL[] = "install";

// Value for uninstallation.
const char PKGMGR_START_UNINSTALL[] = "uninstall";

// Value for update.
const char PKGMGR_START_UPDATE[] = "update";

// Notification about end of installation with status.
const char PKGMGR_END_KEY[] = "end";

// Success value of end of installation.
const char PKGMGR_END_SUCCESS[] = "ok";

// Failure value of end of installation.
const char PKGMGR_END_FAILURE[] = "fail";

const std::string kAppIdPrefix("xwalk.");

class FileDeleter {
 public:
  FileDeleter(const base::FilePath& path, bool recursive)
      : path_(path),
        recursive_(recursive) {}

  ~FileDeleter() {
    if (path_.empty())
      return;

    base::DeleteFile(path_, recursive_);
  }

  void Dismiss() {
    path_.clear();
  }

 private:
  base::FilePath path_;
  bool recursive_;
};

const char* ToEndStatus(bool result) {
  return result ? PKGMGR_END_SUCCESS : PKGMGR_END_FAILURE;
}

}  // namespace

PackageInstallerHelper::PackageInstallerHelper(const std::string& appid)
    : appid_(appid) {
  if (appid_.empty())
    fprintf(stdout, "Invalid app id is provided for pkg installer.\n");

  handle_ = pkgmgr_installer_new();
  if (!handle_)
    fprintf(stdout, "Fail to get package manager installer handle.\n");
}

PackageInstallerHelper::~PackageInstallerHelper() {
  if (handle_)
    pkgmgr_installer_free(handle_);
}

bool PackageInstallerHelper::InstallApplication(
    const std::string& xmlpath,
    const std::string& iconpath) {
  SendSignal(PKGMGR_START_KEY, PKGMGR_START_INSTALL);
  bool ret = InstallApplicationInternal(xmlpath, iconpath);
  SendSignal(PKGMGR_END_KEY, ToEndStatus(ret));
  return ret;
}

bool PackageInstallerHelper::UninstallApplication() {
  SendSignal(PKGMGR_START_KEY, PKGMGR_START_UNINSTALL);
  bool ret = UninstallApplicationInternal();
  SendSignal(PKGMGR_END_KEY, ToEndStatus(ret));
  return ret;
}

bool PackageInstallerHelper::UpdateApplication(
    const std::string& xmlpath,
    const std::string& iconpath) {
  SendSignal(PKGMGR_START_KEY, PKGMGR_START_UPDATE);
  bool ret = UpdateApplicationInternal(xmlpath, iconpath);
  SendSignal(PKGMGR_END_KEY, ToEndStatus(ret));
  return ret;
}

bool PackageInstallerHelper::InstallApplicationInternal(
    const std::string& xmlpath,
    const std::string& iconpath) {
  if (xmlpath.empty() || iconpath.empty()) {
    fprintf(stdout, "Invalid xml path or icon path for installation\n");
  }

  base::FilePath icon_src(iconpath);
  // icon_dst == /opt/share/icons/default/small/xwalk-service.<appid>.png
  // FIXME(vcgomes): Add support for more icon types
  base::FilePath icon_dst = kIconDir.Append(
      kServicePrefix + std::string(appid_) + ".png");
  if (!base::CopyFile(icon_src, icon_dst)) {
    fprintf(stdout, "Couldn't copy application icon to '%s'\n",
            icon_dst.value().c_str());
    return false;
  }

  FileDeleter icon_cleaner(icon_dst, false);

  base::FilePath xml_src(xmlpath);
  base::FilePath xml_dst = kXmlDir.Append(
      kServicePrefix + std::string(appid_) + ".xml");
  if (!base::CopyFile(xml_src, xml_dst)) {
    fprintf(stdout, "Couldn't copy application XML metadata to '%s'\n",
            xml_dst.value().c_str());
    return false;
  }

  if (pkgmgr_parser_parse_manifest_for_installation(xmlpath.c_str(), NULL)) {
    fprintf(stdout, "Couldn't parse manifest XML '%s'\n", xmlpath.c_str());
    return false;
  }

  FileDeleter xml_cleaner(xml_dst, false);
  icon_cleaner.Dismiss();
  xml_cleaner.Dismiss();

  return true;
}

bool PackageInstallerHelper::UninstallApplicationInternal() {
  bool result = true;

  // FIXME(vcgomes): Add support for more icon types
  base::FilePath icon_dst = kIconDir.Append(
      kServicePrefix + appid_ + ".png");
  if (!base::DeleteFile(icon_dst, false)) {
    fprintf(stdout, "Couldn't delete '%s'\n", icon_dst.value().c_str());
    result = false;
  }

  base::FilePath xmlpath(kXmlDir);
  xmlpath = xmlpath.Append(kServicePrefix + std::string(appid_) + ".xml");

  std::string xmlpath_str = xmlpath.MaybeAsASCII();
  assert(!xmlpath_str.empty());
  if (pkgmgr_parser_parse_manifest_for_uninstallation(
        xmlpath_str.c_str(), NULL)) {
    fprintf(stdout, "Couldn't parse manifest XML '%s'\n", xmlpath_str.c_str());
    result = false;
  }

  if (!base::DeleteFile(xmlpath, false)) {
    fprintf(stdout, "Couldn't delete '%s'\n", xmlpath_str.c_str());
    result = false;
  }

  return result;
}

bool PackageInstallerHelper::UpdateApplicationInternal(
    const std::string& xmlpath,
    const std::string& iconpath) {
  if (xmlpath.empty() || iconpath.empty()) {
    fprintf(stdout, "Invalid xml path or icon path for update\n");
  }

  base::FilePath icon_src(iconpath);
  // icon_dst == /opt/share/icons/default/small/xwalk-service.<appid>.png
  // FIXME(vcgomes): Add support for more icon types
  base::FilePath icon_dst = kIconDir.Append(
      kServicePrefix + std::string(appid_) + ".png");
  if (!base::CopyFile(icon_src, icon_dst)) {
    fprintf(stdout, "Couldn't copy application icon to '%s'\n",
            icon_dst.value().c_str());
    return false;
  }

  FileDeleter icon_cleaner(icon_dst, false);

  base::FilePath xml_src(xmlpath);
  base::FilePath xml_dst = kXmlDir.Append(
      kServicePrefix + std::string(appid_) + ".xml");
  if (!base::CopyFile(xml_src, xml_dst)) {
    fprintf(stdout, "Couldn't copy application XML metadata to '%s'\n",
            xml_dst.value().c_str());
    return false;
  }

  if (pkgmgr_parser_parse_manifest_for_upgrade(xmlpath.c_str(), NULL)) {
    fprintf(stdout, "Couldn't parse manifest XML '%s'\n", xmlpath.c_str());
    return false;
  }

  FileDeleter xml_cleaner(xml_dst, false);
  icon_cleaner.Dismiss();
  xml_cleaner.Dismiss();

  return true;
}

bool PackageInstallerHelper::SendSignal(
    const std::string& key,
    const std::string& value) {
  if (!handle_) {
    fprintf(stdout, "The package install manager is not initialized.\n");
    return false;
  }

  if (key.empty() || value.empty()) {
    fprintf(stdout, " Fail to send signal, key/value is empty.\n");
    return false;
  }

  if (pkgmgr_installer_send_signal(
          handle_, PKGMGR_PKG_TYPE, appid_.c_str(),
          key.c_str(), value.c_str())) {
    fprintf(stdout, "Fail to send package manager signal.\n");
  }

  return true;
}
<commit_msg>[Tizen][Runtime] Reconstruct some code on pkg helper.<commit_after>// Copyright (c) 2014 Intel Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "xwalk/application/tools/tizen/xwalk_package_installer_helper.h"

#include <assert.h>
#include <stdio.h>
#include <pkgmgr/pkgmgr_parser.h>

#undef LOG
#include <string>
#include "base/files/file_path.h"
#include "base/file_util.h"

namespace {

typedef int (*PkgParser)(const char*, char* const*);

const base::FilePath kIconDir("/opt/share/icons/default/small/");
const base::FilePath kXmlDir("/opt/share/packages/");
const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
const std::string kServicePrefix("xwalk-service.");
const std::string kXmlFileExt(".xml");
const std::string kPngFileExt(".png");

// Package type sent in every signal.
const char PKGMGR_PKG_TYPE[] = "rpm";

// Notification about operation start.
const char PKGMGR_START_KEY[] = "start";

// Value for new installation.
const char PKGMGR_START_INSTALL[] = "install";

// Value for uninstallation.
const char PKGMGR_START_UNINSTALL[] = "uninstall";

// Value for update.
const char PKGMGR_START_UPDATE[] = "update";

// Notification about end of installation with status.
const char PKGMGR_END_KEY[] = "end";

// Success value of end of installation.
const char PKGMGR_END_SUCCESS[] = "ok";

// Failure value of end of installation.
const char PKGMGR_END_FAILURE[] = "fail";

const std::string kAppIdPrefix("xwalk.");

class FileDeleter {
 public:
  FileDeleter(const base::FilePath& path, bool recursive)
      : path_(path),
        recursive_(recursive) {}

  ~FileDeleter() {
    if (path_.empty())
      return;

    base::DeleteFile(path_, recursive_);
  }

  void Dismiss() {
    path_.clear();
  }

 private:
  base::FilePath path_;
  bool recursive_;
};

const char* ToEndStatus(bool result) {
  return result ? PKGMGR_END_SUCCESS : PKGMGR_END_FAILURE;
}

inline base::FilePath GetDestFilePath(const base::FilePath& DirPath,
                                      const std::string& appid,
                                      const std::string& file_ext) {
  return DirPath.Append(kServicePrefix + std::string(appid) + file_ext);
}

bool CopyFileToDst(const base::FilePath& file_src,
                   const base::FilePath& file_dst) {
  if (!base::CopyFile(file_src, file_dst)) {
    fprintf(stdout, "Couldn't copy application file to '%s'\n",
            file_dst.value().c_str());
    return false;
  }
  return true;
}

}  // namespace

PackageInstallerHelper::PackageInstallerHelper(const std::string& appid)
    : appid_(appid) {
  if (appid_.empty())
    fprintf(stdout, "Invalid app id is provided for pkg installer.\n");

  handle_ = pkgmgr_installer_new();
  if (!handle_)
    fprintf(stdout, "Fail to get package manager installer handle.\n");
}

PackageInstallerHelper::~PackageInstallerHelper() {
  if (handle_)
    pkgmgr_installer_free(handle_);
}

bool PackageInstallerHelper::InstallApplication(
    const std::string& xmlpath,
    const std::string& iconpath) {
  SendSignal(PKGMGR_START_KEY, PKGMGR_START_INSTALL);
  bool ret = InstallApplicationInternal(xmlpath, iconpath);
  SendSignal(PKGMGR_END_KEY, ToEndStatus(ret));
  return ret;
}

bool PackageInstallerHelper::UninstallApplication() {
  SendSignal(PKGMGR_START_KEY, PKGMGR_START_UNINSTALL);
  bool ret = UninstallApplicationInternal();
  SendSignal(PKGMGR_END_KEY, ToEndStatus(ret));
  return ret;
}

bool PackageInstallerHelper::UpdateApplication(
    const std::string& xmlpath,
    const std::string& iconpath) {
  SendSignal(PKGMGR_START_KEY, PKGMGR_START_UPDATE);
  bool ret = UpdateApplicationInternal(xmlpath, iconpath);
  SendSignal(PKGMGR_END_KEY, ToEndStatus(ret));
  return ret;
}

bool PackageInstallerHelper::InstallApplicationInternal(
    const std::string& xmlpath,
    const std::string& iconpath) {
  if (xmlpath.empty() || iconpath.empty()) {
    fprintf(stdout, "Invalid xml path or icon path for installation\n");
  }

  // FIXME(vcgomes): Add support for more icon types
  base::FilePath xml_dst = GetDestFilePath(kXmlDir, appid_, kXmlFileExt);
  base::FilePath icon_dst = GetDestFilePath(kIconDir, appid_, kPngFileExt);
  FileDeleter xml_cleaner(xml_dst, false);
  FileDeleter icon_cleaner(icon_dst, false);

  if (!CopyFileToDst(base::FilePath(xmlpath), xml_dst)
     || !CopyFileToDst(base::FilePath(iconpath), icon_dst))
    return false;

  if (pkgmgr_parser_parse_manifest_for_installation(xmlpath.c_str(), NULL)) {
    fprintf(stdout, "Couldn't parse manifest XML '%s'\n", xmlpath.c_str());
    return false;
  }

  xml_cleaner.Dismiss();
  icon_cleaner.Dismiss();

  return true;
}

bool PackageInstallerHelper::UninstallApplicationInternal() {
  bool result = true;

  // FIXME(vcgomes): Add support for more icon types
  base::FilePath iconpath = GetDestFilePath(kIconDir, appid_, kPngFileExt);
  base::FilePath xmlpath = GetDestFilePath(kXmlDir, appid_, kXmlFileExt);
  FileDeleter icon_cleaner(iconpath, false);
  FileDeleter xml_cleaner(xmlpath, false);

  std::string xmlpath_str = xmlpath.MaybeAsASCII();
  assert(!xmlpath_str.empty());
  if (pkgmgr_parser_parse_manifest_for_uninstallation(
        xmlpath_str.c_str(), NULL)) {
    fprintf(stdout, "Couldn't parse manifest XML '%s'\n", xmlpath_str.c_str());
    icon_cleaner.Dismiss();
    xml_cleaner.Dismiss();
    return false;
  }
  return true;
}

bool PackageInstallerHelper::UpdateApplicationInternal(
    const std::string& xmlpath,
    const std::string& iconpath) {
  if (xmlpath.empty() || iconpath.empty()) {
    fprintf(stdout, "Invalid xml path or icon path for update\n");
  }

  // FIXME(vcgomes): Add support for more icon types
  base::FilePath xml_dst = GetDestFilePath(kXmlDir, appid_, kXmlFileExt);
  base::FilePath icon_dst = GetDestFilePath(kIconDir, appid_, kPngFileExt);
  FileDeleter xml_cleaner(xml_dst, false);
  FileDeleter icon_cleaner(icon_dst, false);

  if (!CopyFileToDst(base::FilePath(xmlpath), xml_dst)
     || !CopyFileToDst(base::FilePath(iconpath), icon_dst))
    return false;

  if (pkgmgr_parser_parse_manifest_for_upgrade(xmlpath.c_str(), NULL)) {
    fprintf(stdout, "Couldn't parse manifest XML '%s'\n", xmlpath.c_str());
    return false;
  }

  xml_cleaner.Dismiss();
  icon_cleaner.Dismiss();

  return true;
}

bool PackageInstallerHelper::SendSignal(
    const std::string& key,
    const std::string& value) {
  if (!handle_) {
    fprintf(stdout, "The package install manager is not initialized.\n");
    return false;
  }

  if (key.empty() || value.empty()) {
    fprintf(stdout, " Fail to send signal, key/value is empty.\n");
    return false;
  }

  if (pkgmgr_installer_send_signal(
          handle_, PKGMGR_PKG_TYPE, appid_.c_str(),
          key.c_str(), value.c_str())) {
    fprintf(stdout, "Fail to send package manager signal.\n");
  }

  return true;
}
<|endoftext|>