@@ -24,7 +24,6 @@ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF S
*/

#include "stdafx.h"
//#include "V41/SnarlInterface.h"
#include "../../SnarlInterface_V42/SnarlInterface.h"
#include "config.h"
#include <map>
@@ -49,62 +48,45 @@ string8 foobarIcon;

Snarl::V42::SnarlInterface sn42;
string8 snarl_password;
UINT SNARL_GLOBAL_MSG = 0;
HWND hwndFooSnarlMsg;
std::map<int,char *> FSMsgClassDecode;
LONG32 lastClassMsg[4] = {0,0,0,0};
LONG32 lastMsg = 0;

int FSLastMsgClass = 0;
int FSMsgClassCount = 4;
metadb_handle_ptr lastSong;

enum FSMsgClass : int {
Stop = 0,
Play,
Pause,
Seek
};

//enum FSMsgAction : int {
// Back = 1,
// Next,
// Stop,
//};

inline char base64_char(unsigned char in)
{
static const char base64_chars[64] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'};
return base64_chars[in];
}

void base64_encode(pfc::string_base & out, const unsigned char * data, unsigned size)
{
out.reset();
#pragma region Declarations
static void try_register();
static void try_unregister();
inline char base64_char(unsigned char in);
void base64_encode(pfc::string_base & out, const unsigned char * data, unsigned size);
#pragma endregion

while (size >= 3)
{
out.add_byte( base64_char( data[0] >> 2 ) );
out.add_byte( base64_char( ( ( data[0] & 3 ) << 4 ) + ( data[1] >> 4 ) ) );
out.add_byte( base64_char( ( ( data[1] & 15 ) << 2 ) + ( data[2] >> 6 ) ) );
out.add_byte( base64_char( data[2] & 63 ) );
data += 3;
size -= 3;
}
if (size == 2)
{
out.add_byte( base64_char( data[0] >> 2 ) );
out.add_byte( base64_char( ( ( data[0] & 3 ) << 4 ) + ( data[1] >> 4 ) ) );
out.add_byte( base64_char( ( data[1] & 15 ) << 2 ) );
out.add_byte( '%' );
}
else if (size == 1)
{
out.add_byte( base64_char( data[0] >> 2 ) );
out.add_byte( base64_char( ( data[0] & 3 ) << 4 ) );
out.add_byte( '%' );
out.add_byte( '%' );
LPSTR FSClass(int intclass){
switch(intclass){
case FSMsgClass::Play:
return "Play";
break;
case FSMsgClass::Pause:
return "Pause";
break;
case FSMsgClass::Stop:
return "Stop";
break;
case FSMsgClass::Seek:
return "Seek";
break;
}
return "";
}

void FSRegisterClass(int intClass){
@@ -117,51 +99,7 @@ void FSRegisterClass(int intClass){
}
}

void try_register()
{
//Register Foobar2000 with Snarl

service_ptr_t<genrand_service> g_rand = genrand_service::g_create();
g_rand->seed( time( NULL ) );
array_t<unsigned> junk;
junk.set_count( 4 );
for ( unsigned i = 0; i < 4; i++ ) junk[ i ] = g_rand->genrand( ~0 );
base64_encode( snarl_password, junk.get_ptr(), 16 );

LONG32 ret = sn42.Register("Foobar2000", "Foobar2000", foobarIcon,snarl_password.get_ptr(),hwndFooSnarlMsg, WM_USER);

if (ret > 0)
{
FSRegisterClass(Play);
FSRegisterClass(Pause);
FSRegisterClass(Stop);
FSRegisterClass(Seek);
}
else
{
if (ret != -Snarl::V42::SnarlEnums::ErrorNotRunning)
{
console::formatter() << "[FooSnarl] Unable to register with Snarl";
}
}
}

void try_unregister()
{
//Unregister foosnarl
LONG32 ret = sn42.ClearClasses();
if (ret < 0 && ret != -Snarl::V42::SnarlEnums::ErrorNotRunning)
{
console::formatter() << "[FooSnarl] Failed to remove registered classes";
}

ret = sn42.Unregister("Foobar2000");
if (ret < 0 && ret != -Snarl::V42::SnarlEnums::ErrorNotRunning)
{
console::formatter() << "[FooSnarl] Failed to unregister with Snarl";
}
}

#pragma region Callback Window
LRESULT CALLBACK WndProcFooSnarl(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
switch(message)
{
@@ -172,7 +110,7 @@ LRESULT CALLBACK WndProcFooSnarl(HWND hwnd, UINT message, WPARAM wParam, LPARAM
case WM_CLOSE:
DestroyWindow(hwnd);
default:
if (message == ::SNARL_GLOBAL_MSG)
if (message == sn42.Broadcast())
{
switch(wParam)
{
@@ -232,245 +170,338 @@ LRESULT CALLBACK WndProcFooSnarl(HWND hwnd, UINT message, WPARAM wParam, LPARAM
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
#pragma endregion

class FooSnarl : public initquit, public play_callback{
public:
#pragma region Play Callback
class play_callback_foosnarl : public play_callback {
protected:
void on_playback_event(int alertClass){
static_api_ptr_t<playback_control> pc;
metadb_handle_ptr handle;
string8 format;
service_ptr_t<titleformat_object> script;
string_formatter text;
string snarl_title;
string snarl_msg;
string snarl_icon;
string8 snarl_icon_data;
long snarl_time;

if(pc->get_now_playing(handle)){
lastSong.copy(handle);
} else {
handle.copy(lastSong);
}

// initquit metods
void on_init()
{
string8 image;
static_api_ptr_t<play_callback_manager> playCBM;
static_api_ptr_t<ui_control> uiMain;
WNDCLASSEX wcex = {0};
if (handle.is_empty()) return;

wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = (WNDPROC) WndProcFooSnarl;
wcex.hInstance = core_api::get_my_instance();
wcex.lpszClassName = _T("FooSnarlMsg");
//Process title format string for message body
g_advconfig_string_format.get_static_instance().get_state(format);
static_api_ptr_t<titleformat_compiler>()->compile_safe(script, format);
pc->playback_format_title_ex(handle, NULL, text, script, NULL, play_control::display_level_titles);
snarl_msg = text.toString();

FSMsgClassDecode[FSMsgClass::Play] = "Play";
FSMsgClassDecode[FSMsgClass::Pause] = "Pause";
FSMsgClassDecode[FSMsgClass::Stop] = "Stop";
FSMsgClassDecode[FSMsgClass::Seek] = "Seek";
//Process title format string for message title
g_advconfig_string_title_format.get_static_instance().get_state(format);
static_api_ptr_t<titleformat_compiler>()->compile_safe(script, format);
pc->playback_format_title_ex(handle, NULL, text, script, NULL, play_control::display_level_titles);
snarl_title = text.toString();

RegisterClassEx(&wcex);

//Snippet for testing Message Window failures
//LPTSTR lpszFunction = TEXT("RegisterClassEx");
// LPVOID lpMsgBuf;
// LPVOID lpDisplayBuf;
// DWORD dw = GetLastError();

// FormatMessage(
// FORMAT_MESSAGE_ALLOCATE_BUFFER |
// FORMAT_MESSAGE_FROM_SYSTEM |
// FORMAT_MESSAGE_IGNORE_INSERTS,
// NULL,
// dw,
// MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
// (LPTSTR) &lpMsgBuf,
// 0, NULL );

// // Display the error message and exit the process

// lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
// (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
// StringCchPrintf((LPTSTR)lpDisplayBuf,
// LocalSize(lpDisplayBuf) / sizeof(TCHAR),
// TEXT("%s failed with error %d: %s"),
// lpszFunction, dw, lpMsgBuf);
// MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

// LocalFree(lpMsgBuf);
// LocalFree(lpDisplayBuf);

uGetModuleFileName(NULL, foobarIcon);
foobarIcon += ",105";

::SNARL_GLOBAL_MSG = sn42.Broadcast();

hwndFooSnarlMsg = CreateWindowEx(0,_T("FooSnarlMsg"),_T("FooSnarl Msg"),0,0,0,0,0,GetDesktopWindow(),0,0,0);

if(hwndFooSnarlMsg == NULL)
{
console::formatter() << "[FooSnarl] Unable to create message window (Error 0x" << pfc::format_int(GetLastError(),0,16) << ")";
metadb_handle_list handle_list;
list_t<GUID> guid_list;
handle_list.add_item(handle);
guid_list.add_item( album_art_ids::cover_front );
try
{
//Get front cover album art if available
abort_callback_impl moo;
album_art_extractor_instance_v2::ptr art_instance = static_api_ptr_t<album_art_manager_v2>()->open( handle_list, guid_list, moo );
album_art_data_ptr art = art_instance->query( album_art_ids::cover_front, moo );
if ( art->get_size() )
{
base64_encode( snarl_icon_data, art->get_ptr(), art->get_size() );
snarl_icon = "";
}
}
catch (...)
{
//Process title format string for message icon location
g_advconfig_icon.get_static_instance().get_state(format);
static_api_ptr_t<titleformat_compiler>()->compile_safe(script, format);
pc->playback_format_title_ex(handle, NULL, text, script, NULL, play_control::display_level_titles);
snarl_icon = text.toString();
}

//Test for existance of folder.jpg picture file
/*DWORD attrib = GetFileAttributes(stringcvt::string_os_from_utf8(snarl_icon.get_ptr()));
if((snarl_icon.get_ptr()=="")||(0xFFFFFFFF == attrib)){
snarl_icon = foobarIcon;
}*/

//Get display timeout from user settings. If invalid, send error. Shouldn't happen max and min are set.
snarl_time = (long) g_advconfig_time.get_static_instance().get_state_int();
if((snarl_time==NULL)||(snarl_time==0)){
snarl_time = 5;
snarl_title = "ERROR";
snarl_msg = "Set valid display time in settings";
}

//Send Snarl Message
if(FSLastMsgClass != alertClass){
sn42.Hide(sn42.GetLastMsgToken());
}
FSLastMsgClass = alertClass;

if (sn42.IsVisible(lastClassMsg[alertClass]) == Snarl::V42::SnarlEnums::Success)
{
sn42.Update(lastClassMsg[alertClass], FSClass(alertClass),snarl_title.get_ptr(),snarl_msg.get_ptr(),snarl_time, snarl_icon.get_ptr(),snarl_icon_data.get_ptr(),0,0,0,0);
}
else
{
LONG32 ret = sn42.Notify(FSClass(alertClass), snarl_title.get_ptr(), snarl_msg.get_ptr(), snarl_time, snarl_icon.get_ptr(), snarl_icon_data.get_ptr(), 0, 0, 0,0);
if (ret > 0)
{
FSAddActions();
}
lastClassMsg[alertClass] = ret;
}
}
//sn.EZNotify("","HWND",(LPCSTR)hwndFooSnarlMsg,5,0,0,0,0);

//Register Playcallback module
playCBM->register_callback(this,flag_on_playback_new_track |
/*flag_on_playback_dynamic_info | */
flag_on_playback_dynamic_info_track |
flag_on_playback_pause |
flag_on_playback_stop,true);
LONG32 FSAddActions(){
LONG32 token = sn42.GetLastMsgToken();
sn42.AddAction(token,"Back","@1");
sn42.AddAction(token,"Next","@2");
sn42.AddAction(token,"Stop","@3");
return 0;
}

try_register();
public:
// play_callback methods
void on_playback_starting(play_control::t_track_command p_command,bool p_paused) {}
void on_playback_new_track(metadb_handle_ptr p_track) {
on_playback_event(FSMsgClass::Play);
}
void on_playback_stop(play_control::t_stop_reason p_reason) {
if(p_reason == play_control::stop_reason_eof || p_reason == play_control::stop_reason_user){
on_playback_event(Stop);
}
}
void on_playback_seek(double p_time) {}
void on_playback_pause(bool p_state) {
if(p_state == true){
on_playback_event(FSMsgClass::Pause);
} else {
on_playback_event(FSMsgClass::Play);
}
}
void on_playback_edited(metadb_handle_ptr p_track) {}
void on_playback_dynamic_info(const file_info & p_info) {}
void on_playback_dynamic_info_track(const file_info &p_info) {
on_playback_event(FSMsgClass::Play);
}
void on_playback_time(double p_time) {}
void on_volume_change(float p_new_val) {}
};

void on_quit()
{
//Unregister playcallback
static_api_ptr_t<play_callback_manager>()->unregister_callback(this);
static play_callback_foosnarl pcb_foosnarl;

try_unregister();
#pragma endregion

lastSong = 0;
DestroyWindow(hwndFooSnarlMsg);
UnregisterClass(_T("FooSnarlMsg"),core_api::get_my_instance());
#pragma region InitQuit
class initquit_foosnarl : public initquit {

if ( temp_file.get_length() ) uDeleteFile(temp_file);
}
public:
void on_init()
{
string8 image;

static_api_ptr_t<ui_control> uiMain;
WNDCLASSEX wcex = {0};

wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = (WNDPROC) WndProcFooSnarl;
wcex.hInstance = core_api::get_my_instance();
wcex.lpszClassName = _T("FooSnarlMsg");

protected:
metadb_handle_ptr lastSong;
string8 temp_file;

void on_playback_event(int alertClass){
static_api_ptr_t<playback_control> pc;
metadb_handle_ptr handle;
string8 format;
service_ptr_t<titleformat_object> script;
string_formatter text;
string snarl_title;
string snarl_msg;
string snarl_icon;
string8 snarl_icon_data;
long snarl_time;

if(pc->get_now_playing(handle)){
lastSong.copy(handle);
} else {
handle.copy(lastSong);
}
FSMsgClassDecode[FSMsgClass::Play] = "Play";
FSMsgClassDecode[FSMsgClass::Pause] = "Pause";
FSMsgClassDecode[FSMsgClass::Stop] = "Stop";
FSMsgClassDecode[FSMsgClass::Seek] = "Seek";

if (handle.is_empty()) return;
RegisterClassEx(&wcex);

//Process title format string for message body
g_advconfig_string_format.get_static_instance().get_state(format);
static_api_ptr_t<titleformat_compiler>()->compile_safe(script, format);
pc->playback_format_title_ex(handle, NULL, text, script, NULL, play_control::display_level_titles);
snarl_msg = text.toString();

//Process title format string for message title
g_advconfig_string_title_format.get_static_instance().get_state(format);
static_api_ptr_t<titleformat_compiler>()->compile_safe(script, format);
pc->playback_format_title_ex(handle, NULL, text, script, NULL, play_control::display_level_titles);
snarl_title = text.toString();

metadb_handle_list handle_list;
list_t<GUID> guid_list;
handle_list.add_item(handle);
guid_list.add_item( album_art_ids::cover_front );
try
{
//Get front cover album art if available
abort_callback_impl moo;
album_art_extractor_instance_v2::ptr art_instance = static_api_ptr_t<album_art_manager_v2>()->open( handle_list, guid_list, moo );
album_art_data_ptr art = art_instance->query( album_art_ids::cover_front, moo );
if ( art->get_size() )
uGetModuleFileName(NULL, foobarIcon);
foobarIcon += ",105";

hwndFooSnarlMsg = CreateWindowEx(0,_T("FooSnarlMsg"),_T("FooSnarl Msg"),0,0,0,0,0,GetDesktopWindow(),0,0,0);

if(hwndFooSnarlMsg == NULL)
{
base64_encode( snarl_icon_data, art->get_ptr(), art->get_size() );
snarl_icon = "";
console::formatter() << "[FooSnarl] Unable to create message window (Error 0x" << pfc::format_int(GetLastError(),0,16) << ")";
}

try_register();
}
catch (...)

void on_quit()
{
//Process title format string for message icon location
g_advconfig_icon.get_static_instance().get_state(format);
static_api_ptr_t<titleformat_compiler>()->compile_safe(script, format);
pc->playback_format_title_ex(handle, NULL, text, script, NULL, play_control::display_level_titles);
snarl_icon = text.toString();
try_unregister();

lastSong = 0;
DestroyWindow(hwndFooSnarlMsg);
UnregisterClass(_T("FooSnarlMsg"),core_api::get_my_instance());
}

//Test for existance of folder.jpg picture file
/*DWORD attrib = GetFileAttributes(stringcvt::string_os_from_utf8(snarl_icon.get_ptr()));
if((snarl_icon.get_ptr()=="")||(0xFFFFFFFF == attrib)){
snarl_icon = foobarIcon;
}*/

//Get display timeout from user settings. If invalid, send error. Shouldn't happen max and min are set.
snarl_time = (long) g_advconfig_time.get_static_instance().get_state_int();
if((snarl_time==NULL)||(snarl_time==0)){
snarl_time = 5;
snarl_title = "ERROR";
snarl_msg = "Set valid display time in settings";
};
#pragma endregion

#pragma region Main Menu
class mainmenu_commands_foosnarl : public mainmenu_commands {

virtual t_uint32 get_command_count(){
return 1;
}

//Send Snarl Message
if(FSLastMsgClass != alertClass){
sn42.Hide(sn42.GetLastMsgToken());
virtual GUID get_command(t_uint32 p_index){
// {E8D1828F-1AB8-4AC9-BDDC-F6DBB1456961}
static const GUID guid_foosnarl_main_showplaying = { 0xe8d1828f, 0x1ab8, 0x4ac9, { 0xbd, 0xdc, 0xf6, 0xdb, 0xb1, 0x45, 0x69, 0x61 } };

if(p_index == 0 )
return guid_foosnarl_main_showplaying;
return pfc::guid_null;
}
FSLastMsgClass = alertClass;

if (sn42.IsVisible(lastClassMsg[alertClass]) == Snarl::V42::SnarlEnums::Success)
//Set p_out to the name of the n-th command.
//This name is used to identify the command and determines
// the default position of the command in the menu.
virtual void get_name(t_uint32 p_index, pfc::string_base & p_out){
if(p_index == 0)
p_out = "Snarl Now Playing";
}

// Set p_out to the description for the n-th command.
virtual bool get_description(t_uint32 p_index, pfc::string_base & p_out){
if(p_index == 0)
p_out = "Snarl Now Playing";
else
return false;
return true;
}

//Every set of commands needs to declare which group it belongs to.
virtual GUID get_parent(){
//return mainmenu_groups::view;
return guid_foosnarl_mainmenu_maingroup;
}

// Execute n-th command.
virtual void execute(t_uint32 p_index, service_ptr_t<service_base> p_callback){
if(p_index == 0 && core_api::assert_main_thread()){
//Execute Snarl Command here
metadb_handle_ptr np;
static_api_ptr_t<playback_control>()->get_now_playing(np);
pcb_foosnarl.on_playback_new_track(np);
}
}
};
#pragma endregion

static void try_register()
{
//Register Playcallback module
static_api_ptr_t<play_callback_manager> playCBM;
playCBM->register_callback(&pcb_foosnarl,play_callback_foosnarl::flag_on_playback_new_track|
/*flag_on_playback_dynamic_info | */
play_callback_foosnarl::flag_on_playback_dynamic_info_track|
play_callback_foosnarl::flag_on_playback_pause|
play_callback_foosnarl::flag_on_playback_stop,true);

//Register Foobar2000 with Snarl
service_ptr_t<genrand_service> g_rand = genrand_service::g_create();
g_rand->seed( time( NULL ) );
array_t<unsigned> junk;
junk.set_count( 4 );
for ( unsigned i = 0; i < 4; i++ ) junk[ i ] = g_rand->genrand( ~0 );
base64_encode( snarl_password, junk.get_ptr(), 16 );

LONG32 ret = sn42.Register("Foobar2000", "Foobar2000", foobarIcon,snarl_password.get_ptr(),hwndFooSnarlMsg, WM_USER);

if (ret > 0)
{
sn42.Update(lastClassMsg[alertClass], FSClass(alertClass),snarl_title.get_ptr(),snarl_msg.get_ptr(),snarl_time, snarl_icon.get_ptr(),snarl_icon_data.get_ptr(),0,0,0,0);
FSRegisterClass(Play);
FSRegisterClass(Pause);
FSRegisterClass(Stop);
FSRegisterClass(Seek);
}
else
{
LONG32 ret = sn42.Notify(FSClass(alertClass), snarl_title.get_ptr(), snarl_msg.get_ptr(), snarl_time, snarl_icon.get_ptr(), snarl_icon_data.get_ptr(), 0, 0, 0,0);
if (ret > 0)
if (ret != -Snarl::V42::SnarlEnums::ErrorNotRunning)
{
FSAddActions();
console::formatter() << "[FooSnarl] Unable to register with Snarl";
}
lastClassMsg[alertClass] = ret;
}
}

LONG32 FSAddActions(){
LONG32 token = sn42.GetLastMsgToken();
sn42.AddAction(token,"Back","@1");
sn42.AddAction(token,"Next","@2");
sn42.AddAction(token,"Stop","@3");
return 0;
}
static void try_unregister()
{
//Unregister playcallback
static_api_ptr_t<play_callback_manager>()->unregister_callback(&pcb_foosnarl);

public:
// play_callback methods
void on_playback_starting(play_control::t_track_command p_command,bool p_paused) {}
void on_playback_new_track(metadb_handle_ptr p_track) {
on_playback_event(FSMsgClass::Play);
}
void on_playback_stop(play_control::t_stop_reason p_reason) {
if(p_reason == play_control::stop_reason_eof || p_reason == play_control::stop_reason_user){
on_playback_event(Stop);
//Unregister foosnarl
LONG32 ret = sn42.ClearClasses();
if (ret < 0 && ret != -Snarl::V42::SnarlEnums::ErrorNotRunning)
{
console::formatter() << "[FooSnarl] Failed to remove registered classes";
}
}
void on_playback_seek(double p_time) {}
void on_playback_pause(bool p_state) {
if(p_state == true){
on_playback_event(FSMsgClass::Pause);
} else {
on_playback_event(FSMsgClass::Play);

ret = sn42.Unregister("Foobar2000");
if (ret < 0 && ret != -Snarl::V42::SnarlEnums::ErrorNotRunning)
{
console::formatter() << "[FooSnarl] Failed to unregister with Snarl";
}
}
void on_playback_edited(metadb_handle_ptr p_track) {}
void on_playback_dynamic_info(const file_info & p_info) {}
void on_playback_dynamic_info_track(const file_info &p_info) {
on_playback_event(FSMsgClass::Play);

#pragma region Utility Functions
inline char base64_char(unsigned char in)
{
static const char base64_chars[64] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'};
return base64_chars[in];
}
void on_playback_time(double p_time) {}
void on_volume_change(float p_new_val) {}
LPSTR FSClass(int intclass){
switch(intclass){
case FSMsgClass::Play:
return "Play";
break;
case FSMsgClass::Pause:
return "Pause";
break;
case FSMsgClass::Stop:
return "Stop";
break;
case FSMsgClass::Seek:
return "Seek";
break;

void base64_encode(pfc::string_base & out, const unsigned char * data, unsigned size)
{
out.reset();

while (size >= 3)
{
out.add_byte( base64_char( data[0] >> 2 ) );
out.add_byte( base64_char( ( ( data[0] & 3 ) << 4 ) + ( data[1] >> 4 ) ) );
out.add_byte( base64_char( ( ( data[1] & 15 ) << 2 ) + ( data[2] >> 6 ) ) );
out.add_byte( base64_char( data[2] & 63 ) );
data += 3;
size -= 3;
}
if (size == 2)
{
out.add_byte( base64_char( data[0] >> 2 ) );
out.add_byte( base64_char( ( ( data[0] & 3 ) << 4 ) + ( data[1] >> 4 ) ) );
out.add_byte( base64_char( ( data[1] & 15 ) << 2 ) );
out.add_byte( '%' );
}
else if (size == 1)
{
out.add_byte( base64_char( data[0] >> 2 ) );
out.add_byte( base64_char( ( data[0] & 3 ) << 4 ) );
out.add_byte( '%' );
out.add_byte( '%' );
}
return "";
}
#pragma endregion

};

//Register initquit
static initquit_factory_t< FooSnarl > foo0;
//Register initquit, menu
static initquit_factory_t< initquit_foosnarl > foo_snarl_initquit;
static mainmenu_commands_factory_t<mainmenu_commands_foosnarl> foo_snarl_menu;
static mainmenu_group_popup_factory mainmenu_group(guid_foosnarl_mainmenu_maingroup, mainmenu_groups::view,mainmenu_commands::sort_priority_dontcare,"FooSnarl");