<commit_before>/// @file
/// @version 3.5
///
/// @section LICENSE
///
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause

#ifdef _ANDROID
#include <jni.h>

#include <hltypes/harray.h>
#include <hltypes/hlog.h>
#include <hltypes/hltypesUtil.h>
#include <hltypes/hresource.h>
#include <hltypes/hstring.h>

#define __NATIVE_INTERFACE_CLASS "com/april/NativeInterface"
#include "androidUtilJNI.h"
#include "april.h"
#include "Keys.h"
#include "main_base.h"
#include "Platform.h"
#include "RenderSystem.h"
#include "Window.h"
#include "AndroidJNI_Keys.h"

#define PROTECTED_WINDOW_CALL(methodCall) \
	if (april::window != NULL) \
	{ \
		april::window->methodCall; \
	}
#define PROTECTED_RENDERSYS_CALL(methodCall) \
	if (april::rendersys != NULL) \
	{ \
		april::rendersys->methodCall; \
	}

namespace april
{
	extern void* javaVM;
	extern void (*dialogCallback)(MessageBoxButton);
	void (*aprilInit)(const harray<hstr>&) = NULL;
	void (*aprilDestroy)() = NULL;
	extern jobject classLoader;

	void JNICALL _JNI_setVariables(JNIEnv* env, jclass classe, jstring jDataPath, jstring jForcedArchivePath)
	{
		hstr dataPath = _JSTR_TO_HSTR(jDataPath);
		hstr archivePath = _JSTR_TO_HSTR(jForcedArchivePath);
		hlog::write(april::logTag, "System path: " + april::getUserDataPath());
		if (!hresource::hasZip()) // if not using APK as data file archive
		{
#ifndef _OPENKODE
			// set the resources CWD
			hresource::setCwd(dataPath + "/Android/data/" + april::getPackageName());
			hresource::setArchive(""); // not used anyway when hasZip() returns false
			hlog::write(april::logTag, "Using no-zip: " + hresource::getCwd());
#else
			hlog::write(april::logTag, "Using KD file system: " + hresource::getCwd());
#endif
		}
		else if (archivePath != "")
		{
			// using APK file as archive
			hresource::setCwd("assets");
			hresource::setArchive(archivePath);
			hlog::write(april::logTag, "Using assets: " + hresource::getArchive());
		}
		else
		{
			// using OBB file as archive
			hresource::setCwd(".");
			// using Google Play's "Expansion File" system
			hresource::setArchive(dataPath);
			hlog::write(april::logTag, "Using obb: " + hresource::getArchive());
		}
	}

	void JNICALL _JNI_init(JNIEnv* env, jclass classe, jobjectArray _args)
	{
		harray<hstr> args;
		int length = env->GetArrayLength(_args);
		jstring jStr;
		for_iter (i, 0, length)
		{
			args += _JSTR_TO_HSTR((jstring)env->GetObjectArrayElement(_args, i));
		}
		hlog::debug(april::logTag, "Got args:");
		foreach (hstr, it, args)
		{
			hlog::debug(april::logTag, "    " + (*it));
		}
		(*aprilInit)(args);
	}

	void JNICALL _JNI_destroy(JNIEnv* env, jclass classe)
	{
		(*aprilDestroy)();
	}

	bool JNICALL _JNI_render(JNIEnv* env, jclass classe)
	{
		bool result = true;
		if (april::window != NULL)
		{
			try
			{
				result = april::window->updateOneFrame();
			}
			catch (hltypes::exception& e)
			{
				hlog::error("FATAL", e.getMessage());
				throw e;
			}
		}
		return true;
	}

	void JNICALL _JNI_onKeyDown(JNIEnv* env, jclass classe, jint keyCode, jint charCode)
	{
		PROTECTED_WINDOW_CALL(queueKeyEvent(april::Window::KEY_DOWN, android2april((int)keyCode), (unsigned int)charCode));
	}

	void JNICALL _JNI_onKeyUp(JNIEnv* env, jclass classe, jint keyCode)
	{
		PROTECTED_WINDOW_CALL(queueKeyEvent(april::Window::KEY_UP, android2april((int)keyCode), 0));
	}

	void JNICALL _JNI_onTouch(JNIEnv* env, jclass classe, jint type, jfloat x, jfloat y, jint index)
	{
		PROTECTED_WINDOW_CALL(queueTouchEvent((april::Window::MouseEventType)type, gvec2((float)x, (float)y), (int)index));
	}

	void JNICALL _JNI_onButtonDown(JNIEnv* env, jclass classe, jint keyCode, jint charCode)
	{
		PROTECTED_WINDOW_CALL(queueControllerEvent(april::Window::CONTROLLER_DOWN, (Button)(int)keyCode));
	}

	void JNICALL _JNI_onButtonUp(JNIEnv* env, jclass classe, jint keyCode)
	{
		PROTECTED_WINDOW_CALL(queueControllerEvent(april::Window::CONTROLLER_UP, (Button)(int)keyCode));
	}

	void JNICALL _JNI_onControllerAxis(JNIEnv* env, jclass classe, jint keyCode, jfloat axisValue)
	{
		PROTECTED_WINDOW_CALL(queueControllerAxisEvent(april::Window::CONTROLLER_AXIS, (Button)(int)keyCode, (float) axisValue));
	}

	void JNICALL _JNI_onWindowFocusChanged(JNIEnv* env, jclass classe, jboolean jFocused)
	{
		bool focused = (jFocused != JNI_FALSE);
		hlog::write(april::logTag, "onWindowFocusChanged(" + hstr(focused) + ")");
		PROTECTED_WINDOW_CALL(handleFocusChangeEvent(focused));
	}

	void JNICALL _JNI_onVirtualKeyboardChanged(JNIEnv* env, jclass classe, jboolean jVisible, jfloat jHeightRatio)
	{
		bool visible = (jVisible != JNI_FALSE);
		float heightRatio = (float)jHeightRatio;
		hlog::write(april::logTag, "onVirtualKeyboardChanged(" + hstr(visible) + "," + hstr(heightRatio) + ")");
		PROTECTED_WINDOW_CALL(handleVirtualKeyboardChangeEvent(visible, heightRatio));
	}

	void JNICALL _JNI_onLowMemory(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "onLowMemoryWarning()");
		PROTECTED_WINDOW_CALL(handleLowMemoryWarning());
	}

	void JNICALL _JNI_onSurfaceCreated(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android View::onSurfaceCreated()");
		PROTECTED_RENDERSYS_CALL(reset());
	}

	void JNICALL _JNI_activityOnCreate(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onCreate()");
	}

	void JNICALL _JNI_activityOnStart(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onStart()");
	}

	void JNICALL _JNI_activityOnResume(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onResume()");
		PROTECTED_WINDOW_CALL(handleActivityChangeEvent(true));
	}

	void JNICALL _JNI_activityOnPause(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onPause()");
		PROTECTED_WINDOW_CALL(handleActivityChangeEvent(false));
		PROTECTED_RENDERSYS_CALL(unloadTextures());
	}

	void JNICALL _JNI_activityOnStop(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onStop()");
	}

	void JNICALL _JNI_activityOnDestroy(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onDestroy()");
		if (april::classLoader != NULL)
		{
			env->DeleteGlobalRef(april::classLoader);
			april::classLoader = NULL;
		}
	}

	void JNICALL _JNI_activityOnRestart(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onRestart()");
	}

	void JNICALL _JNI_onDialogOk(JNIEnv* env, jclass classe)
	{
		if (dialogCallback != NULL)
		{
			(*dialogCallback)(MESSAGE_BUTTON_OK);
		}
	}

	void JNICALL _JNI_onDialogYes(JNIEnv* env, jclass classe)
	{
		if (dialogCallback != NULL)
		{
			(*dialogCallback)(MESSAGE_BUTTON_YES);
		}
	}

	void JNICALL _JNI_onDialogNo(JNIEnv* env, jclass classe)
	{
		if (dialogCallback != NULL)
		{
			(*dialogCallback)(MESSAGE_BUTTON_NO);
		}
	}

	void JNICALL _JNI_onDialogCancel(JNIEnv* env, jclass classe)
	{
		if (dialogCallback != NULL)
		{
			(*dialogCallback)(MESSAGE_BUTTON_CANCEL);
		}
	}

#define METHOD_COUNT 25 // make sure this fits
	static JNINativeMethod methods[METHOD_COUNT] =
	{
		{"setVariables",						_JARGS(_JVOID, _JSTR _JSTR),					(void*)&april::_JNI_setVariables						},
		{"init",								_JARGS(_JVOID, _JARR(_JSTR)),					(void*)&april::_JNI_init								},
		{"destroy",								_JARGS(_JVOID, ),								(void*)&april::_JNI_destroy								},
		{"render",								_JARGS(_JBOOL, ),								(void*)&april::_JNI_render								},
		{"onKeyDown",							_JARGS(_JVOID, _JINT _JINT),					(bool*)&april::_JNI_onKeyDown							},
		{"onKeyUp",								_JARGS(_JVOID, _JINT),							(bool*)&april::_JNI_onKeyUp								},
		{"onTouch",								_JARGS(_JVOID, _JINT _JFLOAT _JFLOAT _JINT),	(void*)&april::_JNI_onTouch								},
		{"onButtonDown",						_JARGS(_JVOID, _JINT),							(bool*)&april::_JNI_onButtonDown						},
		{"onButtonUp",							_JARGS(_JVOID, _JINT),							(bool*)&april::_JNI_onButtonUp							},
		{"onControllerAxis",					_JARGS(_JVOID, _JINT _JFLOAT),					(bool*)&april::_JNI_onControllerAxis					},
		{"onWindowFocusChanged",				_JARGS(_JVOID, _JBOOL),							(void*)&april::_JNI_onWindowFocusChanged				},
		{"onVirtualKeyboardChanged",			_JARGS(_JVOID, _JBOOL _JFLOAT),					(void*)&april::_JNI_onVirtualKeyboardChanged			},
		{"onLowMemory",							_JARGS(_JVOID, ),								(void*)&april::_JNI_onLowMemory							},
		{"onSurfaceCreated",					_JARGS(_JVOID, ),								(void*)&april::_JNI_onSurfaceCreated					},
		{"activityOnCreate",					_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnCreate					},
		{"activityOnStart",						_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnStart						},
		{"activityOnResume",					_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnResume					},
		{"activityOnPause",						_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnPause						},
		{"activityOnStop",						_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnStop						},
		{"activityOnDestroy",					_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnDestroy					},
		{"activityOnRestart",					_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnRestart					},
		{"onDialogOk",							_JARGS(_JVOID, ),								(void*)&april::_JNI_onDialogOk							},
		{"onDialogYes",							_JARGS(_JVOID, ),								(void*)&april::_JNI_onDialogYes							},
		{"onDialogNo",							_JARGS(_JVOID, ),								(void*)&april::_JNI_onDialogNo							},
		{"onDialogCancel",						_JARGS(_JVOID, ),								(void*)&april::_JNI_onDialogCancel						}
	};

	jint JNI_OnLoad(void (*anAprilInit)(const harray<hstr>&), void (*anAprilDestroy)(), JavaVM* vm, void* reserved)
	{
		april::javaVM = (void*)vm;
		april::aprilInit = anAprilInit;
		april::aprilDestroy = anAprilDestroy;
		JNIEnv* env = NULL;
		if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
		{
			return -1;
		}
		jclass classNativeInterface = env->FindClass(__NATIVE_INTERFACE_CLASS);
		if (env->RegisterNatives(classNativeInterface, methods, METHOD_COUNT) != 0)
		{
			return -1;
		}
#ifdef _OPENKODE // not really needed when OpenKODE isn't used
		jclass classClass = env->FindClass("java/lang/Class");
		jmethodID methodGetClassLoader = env->GetMethodID(classClass, "getClassLoader", _JARGS(_JCLASS("java/lang/ClassLoader"), ));
		jobject classLoader = env->CallObjectMethod(classNativeInterface, methodGetClassLoader);
		april::classLoader = env->NewGlobalRef(classLoader);
#endif
		return JNI_VERSION_1_6;
	}

}

#endif
<commit_msg>android update terminate fix<commit_after>/// @file
/// @version 3.5
///
/// @section LICENSE
///
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause

#ifdef _ANDROID
#include <jni.h>

#include <hltypes/harray.h>
#include <hltypes/hlog.h>
#include <hltypes/hltypesUtil.h>
#include <hltypes/hresource.h>
#include <hltypes/hstring.h>

#define __NATIVE_INTERFACE_CLASS "com/april/NativeInterface"
#include "androidUtilJNI.h"
#include "april.h"
#include "Keys.h"
#include "main_base.h"
#include "Platform.h"
#include "RenderSystem.h"
#include "Window.h"
#include "AndroidJNI_Keys.h"

#define PROTECTED_WINDOW_CALL(methodCall) \
	if (april::window != NULL) \
	{ \
		april::window->methodCall; \
	}
#define PROTECTED_RENDERSYS_CALL(methodCall) \
	if (april::rendersys != NULL) \
	{ \
		april::rendersys->methodCall; \
	}

namespace april
{
	extern void* javaVM;
	extern void (*dialogCallback)(MessageBoxButton);
	void (*aprilInit)(const harray<hstr>&) = NULL;
	void (*aprilDestroy)() = NULL;
	extern jobject classLoader;

	void JNICALL _JNI_setVariables(JNIEnv* env, jclass classe, jstring jDataPath, jstring jForcedArchivePath)
	{
		hstr dataPath = _JSTR_TO_HSTR(jDataPath);
		hstr archivePath = _JSTR_TO_HSTR(jForcedArchivePath);
		hlog::write(april::logTag, "System path: " + april::getUserDataPath());
		if (!hresource::hasZip()) // if not using APK as data file archive
		{
#ifndef _OPENKODE
			// set the resources CWD
			hresource::setCwd(dataPath + "/Android/data/" + april::getPackageName());
			hresource::setArchive(""); // not used anyway when hasZip() returns false
			hlog::write(april::logTag, "Using no-zip: " + hresource::getCwd());
#else
			hlog::write(april::logTag, "Using KD file system: " + hresource::getCwd());
#endif
		}
		else if (archivePath != "")
		{
			// using APK file as archive
			hresource::setCwd("assets");
			hresource::setArchive(archivePath);
			hlog::write(april::logTag, "Using assets: " + hresource::getArchive());
		}
		else
		{
			// using OBB file as archive
			hresource::setCwd(".");
			// using Google Play's "Expansion File" system
			hresource::setArchive(dataPath);
			hlog::write(april::logTag, "Using obb: " + hresource::getArchive());
		}
	}

	void JNICALL _JNI_init(JNIEnv* env, jclass classe, jobjectArray _args)
	{
		harray<hstr> args;
		int length = env->GetArrayLength(_args);
		jstring jStr;
		for_iter (i, 0, length)
		{
			args += _JSTR_TO_HSTR((jstring)env->GetObjectArrayElement(_args, i));
		}
		hlog::debug(april::logTag, "Got args:");
		foreach (hstr, it, args)
		{
			hlog::debug(april::logTag, "    " + (*it));
		}
		(*aprilInit)(args);
	}

	void JNICALL _JNI_destroy(JNIEnv* env, jclass classe)
	{
		(*aprilDestroy)();
	}

	bool JNICALL _JNI_render(JNIEnv* env, jclass classe)
	{
		bool result = true;
		if (april::window != NULL)
		{
			try
			{
				result = april::window->updateOneFrame();
			}
			catch (hltypes::exception& e)
			{
				hlog::error("FATAL", e.getMessage());
				throw e;
			}
		}
		return result;
	}

	void JNICALL _JNI_onKeyDown(JNIEnv* env, jclass classe, jint keyCode, jint charCode)
	{
		PROTECTED_WINDOW_CALL(queueKeyEvent(april::Window::KEY_DOWN, android2april((int)keyCode), (unsigned int)charCode));
	}

	void JNICALL _JNI_onKeyUp(JNIEnv* env, jclass classe, jint keyCode)
	{
		PROTECTED_WINDOW_CALL(queueKeyEvent(april::Window::KEY_UP, android2april((int)keyCode), 0));
	}

	void JNICALL _JNI_onTouch(JNIEnv* env, jclass classe, jint type, jfloat x, jfloat y, jint index)
	{
		PROTECTED_WINDOW_CALL(queueTouchEvent((april::Window::MouseEventType)type, gvec2((float)x, (float)y), (int)index));
	}

	void JNICALL _JNI_onButtonDown(JNIEnv* env, jclass classe, jint keyCode, jint charCode)
	{
		PROTECTED_WINDOW_CALL(queueControllerEvent(april::Window::CONTROLLER_DOWN, (Button)(int)keyCode));
	}

	void JNICALL _JNI_onButtonUp(JNIEnv* env, jclass classe, jint keyCode)
	{
		PROTECTED_WINDOW_CALL(queueControllerEvent(april::Window::CONTROLLER_UP, (Button)(int)keyCode));
	}

	void JNICALL _JNI_onControllerAxis(JNIEnv* env, jclass classe, jint keyCode, jfloat axisValue)
	{
		PROTECTED_WINDOW_CALL(queueControllerAxisEvent(april::Window::CONTROLLER_AXIS, (Button)(int)keyCode, (float) axisValue));
	}

	void JNICALL _JNI_onWindowFocusChanged(JNIEnv* env, jclass classe, jboolean jFocused)
	{
		bool focused = (jFocused != JNI_FALSE);
		hlog::write(april::logTag, "onWindowFocusChanged(" + hstr(focused) + ")");
		PROTECTED_WINDOW_CALL(handleFocusChangeEvent(focused));
	}

	void JNICALL _JNI_onVirtualKeyboardChanged(JNIEnv* env, jclass classe, jboolean jVisible, jfloat jHeightRatio)
	{
		bool visible = (jVisible != JNI_FALSE);
		float heightRatio = (float)jHeightRatio;
		hlog::write(april::logTag, "onVirtualKeyboardChanged(" + hstr(visible) + "," + hstr(heightRatio) + ")");
		PROTECTED_WINDOW_CALL(handleVirtualKeyboardChangeEvent(visible, heightRatio));
	}

	void JNICALL _JNI_onLowMemory(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "onLowMemoryWarning()");
		PROTECTED_WINDOW_CALL(handleLowMemoryWarning());
	}

	void JNICALL _JNI_onSurfaceCreated(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android View::onSurfaceCreated()");
		PROTECTED_RENDERSYS_CALL(reset());
	}

	void JNICALL _JNI_activityOnCreate(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onCreate()");
	}

	void JNICALL _JNI_activityOnStart(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onStart()");
	}

	void JNICALL _JNI_activityOnResume(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onResume()");
		PROTECTED_WINDOW_CALL(handleActivityChangeEvent(true));
	}

	void JNICALL _JNI_activityOnPause(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onPause()");
		PROTECTED_WINDOW_CALL(handleActivityChangeEvent(false));
		PROTECTED_RENDERSYS_CALL(unloadTextures());
	}

	void JNICALL _JNI_activityOnStop(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onStop()");
	}

	void JNICALL _JNI_activityOnDestroy(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onDestroy()");
		if (april::classLoader != NULL)
		{
			env->DeleteGlobalRef(april::classLoader);
			april::classLoader = NULL;
		}
	}

	void JNICALL _JNI_activityOnRestart(JNIEnv* env, jclass classe)
	{
		hlog::write(april::logTag, "Android Activity::onRestart()");
	}

	void JNICALL _JNI_onDialogOk(JNIEnv* env, jclass classe)
	{
		if (dialogCallback != NULL)
		{
			(*dialogCallback)(MESSAGE_BUTTON_OK);
		}
	}

	void JNICALL _JNI_onDialogYes(JNIEnv* env, jclass classe)
	{
		if (dialogCallback != NULL)
		{
			(*dialogCallback)(MESSAGE_BUTTON_YES);
		}
	}

	void JNICALL _JNI_onDialogNo(JNIEnv* env, jclass classe)
	{
		if (dialogCallback != NULL)
		{
			(*dialogCallback)(MESSAGE_BUTTON_NO);
		}
	}

	void JNICALL _JNI_onDialogCancel(JNIEnv* env, jclass classe)
	{
		if (dialogCallback != NULL)
		{
			(*dialogCallback)(MESSAGE_BUTTON_CANCEL);
		}
	}

#define METHOD_COUNT 25 // make sure this fits
	static JNINativeMethod methods[METHOD_COUNT] =
	{
		{"setVariables",						_JARGS(_JVOID, _JSTR _JSTR),					(void*)&april::_JNI_setVariables						},
		{"init",								_JARGS(_JVOID, _JARR(_JSTR)),					(void*)&april::_JNI_init								},
		{"destroy",								_JARGS(_JVOID, ),								(void*)&april::_JNI_destroy								},
		{"render",								_JARGS(_JBOOL, ),								(void*)&april::_JNI_render								},
		{"onKeyDown",							_JARGS(_JVOID, _JINT _JINT),					(bool*)&april::_JNI_onKeyDown							},
		{"onKeyUp",								_JARGS(_JVOID, _JINT),							(bool*)&april::_JNI_onKeyUp								},
		{"onTouch",								_JARGS(_JVOID, _JINT _JFLOAT _JFLOAT _JINT),	(void*)&april::_JNI_onTouch								},
		{"onButtonDown",						_JARGS(_JVOID, _JINT),							(bool*)&april::_JNI_onButtonDown						},
		{"onButtonUp",							_JARGS(_JVOID, _JINT),							(bool*)&april::_JNI_onButtonUp							},
		{"onControllerAxis",					_JARGS(_JVOID, _JINT _JFLOAT),					(bool*)&april::_JNI_onControllerAxis					},
		{"onWindowFocusChanged",				_JARGS(_JVOID, _JBOOL),							(void*)&april::_JNI_onWindowFocusChanged				},
		{"onVirtualKeyboardChanged",			_JARGS(_JVOID, _JBOOL _JFLOAT),					(void*)&april::_JNI_onVirtualKeyboardChanged			},
		{"onLowMemory",							_JARGS(_JVOID, ),								(void*)&april::_JNI_onLowMemory							},
		{"onSurfaceCreated",					_JARGS(_JVOID, ),								(void*)&april::_JNI_onSurfaceCreated					},
		{"activityOnCreate",					_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnCreate					},
		{"activityOnStart",						_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnStart						},
		{"activityOnResume",					_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnResume					},
		{"activityOnPause",						_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnPause						},
		{"activityOnStop",						_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnStop						},
		{"activityOnDestroy",					_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnDestroy					},
		{"activityOnRestart",					_JARGS(_JVOID, ),								(void*)&april::_JNI_activityOnRestart					},
		{"onDialogOk",							_JARGS(_JVOID, ),								(void*)&april::_JNI_onDialogOk							},
		{"onDialogYes",							_JARGS(_JVOID, ),								(void*)&april::_JNI_onDialogYes							},
		{"onDialogNo",							_JARGS(_JVOID, ),								(void*)&april::_JNI_onDialogNo							},
		{"onDialogCancel",						_JARGS(_JVOID, ),								(void*)&april::_JNI_onDialogCancel						}
	};

	jint JNI_OnLoad(void (*anAprilInit)(const harray<hstr>&), void (*anAprilDestroy)(), JavaVM* vm, void* reserved)
	{
		april::javaVM = (void*)vm;
		april::aprilInit = anAprilInit;
		april::aprilDestroy = anAprilDestroy;
		JNIEnv* env = NULL;
		if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
		{
			return -1;
		}
		jclass classNativeInterface = env->FindClass(__NATIVE_INTERFACE_CLASS);
		if (env->RegisterNatives(classNativeInterface, methods, METHOD_COUNT) != 0)
		{
			return -1;
		}
#ifdef _OPENKODE // not really needed when OpenKODE isn't used
		jclass classClass = env->FindClass("java/lang/Class");
		jmethodID methodGetClassLoader = env->GetMethodID(classClass, "getClassLoader", _JARGS(_JCLASS("java/lang/ClassLoader"), ));
		jobject classLoader = env->CallObjectMethod(classNativeInterface, methodGetClassLoader);
		april::classLoader = env->NewGlobalRef(classLoader);
#endif
		return JNI_VERSION_1_6;
	}

}

#endif
<|endoftext|>


<commit_before>/* FLAC input plugin for Winamp3
 * Copyright (C) 2000,2001,2002  Josh Coalson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * NOTE: this code is derived from the 'rawpcm' example by
 * Nullsoft; the original license for the 'rawpcm' example follows.
 */
/*

  Nullsoft WASABI Source File License

  Copyright 1999-2001 Nullsoft, Inc.

    This software is provided 'as-is', without any express or implied
    warranty.  In no event will the authors be held liable for any damages
    arising from the use of this software.

    Permission is granted to anyone to use this software for any purpose,
    including commercial applications, and to alter it and redistribute it
    freely, subject to the following restrictions:

    1. The origin of this software must not be misrepresented; you must not
       claim that you wrote the original software. If you use this software
       in a product, an acknowledgment in the product documentation would be
       appreciated but is not required.
    2. Altered source versions must be plainly marked as such, and must not be
       misrepresented as being the original software.
    3. This notice may not be removed or altered from any source distribution.

  Brennan Underwood
  brennan@nullsoft.com

*/

#include "flacpcm.h"
extern "C" {
#include "FLAC/utility.h"
};

struct id3v1_struct {
	FLAC__byte raw[128];
	char title[31];
	char artist[31];
	char album[31];
	char comment[31];
	unsigned year;
	unsigned track; /* may be 0 if v1 (not v1.1) tag */
	unsigned genre;
	char description[1024]; /* the formatted description passed to the gui */
};

static bool get_id3v1_tag_(const char *filename, id3v1_struct *tag);

FlacPcm::FlacPcm():
needs_seek(false),
seek_sample(0),
samples_in_reservoir(0),
abort_flag(false),
reader(0),
decoder(0)
{
}

FlacPcm::~FlacPcm()
{
	cleanup();
}

int FlacPcm::getInfos(MediaInfo *infos)
{
	reader = infos->getReader();
	if(!reader) return 0;

	//@@@ to be really "clean" we should go through the reader instead of directly to the file...
	if(!FLAC__utility_get_streaminfo(infos->getFilename(), &stream_info))
		return 1;

	id3v1_struct tag;

	//@@@ ditto here...
	bool has_tag = get_id3v1_tag_(infos->getFilename(), &tag);

	infos->setLength(lengthInMsec());
	//@@@ infos->setTitle(Std::filename(infos->getFilename()));
	infos->setTitle(tag.description);
	infos->setInfo(StringPrintf("FLAC:<%ihz:%ibps:%dch>", stream_info.sample_rate, stream_info.bits_per_sample, stream_info.channels)); //@@@ fix later
	if(has_tag) {
		infos->setData("Title", tag.title);
		infos->setData("Artist", tag.artist);
		infos->setData("Album", tag.album);
	}

	return 0;
}

int FlacPcm::processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch)
{
	reader = infos->getReader();
	if(reader == 0)
		return 0;

	if(decoder == 0) {
		decoder = FLAC__seekable_stream_decoder_new();
		if(decoder == 0)
			return 0;
		FLAC__seekable_stream_decoder_set_md5_checking(decoder, false);
		FLAC__seekable_stream_decoder_set_read_callback(decoder, readCallback_);
		FLAC__seekable_stream_decoder_set_seek_callback(decoder, seekCallback_);
		FLAC__seekable_stream_decoder_set_tell_callback(decoder, tellCallback_);
		FLAC__seekable_stream_decoder_set_length_callback(decoder, lengthCallback_);
		FLAC__seekable_stream_decoder_set_eof_callback(decoder, eofCallback_);
		FLAC__seekable_stream_decoder_set_write_callback(decoder, writeCallback_);
		FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadataCallback_);
		FLAC__seekable_stream_decoder_set_error_callback(decoder, errorCallback_);
		FLAC__seekable_stream_decoder_set_client_data(decoder, this);

		if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) {
			cleanup();
			return 0;
		}
		if(!FLAC__seekable_stream_decoder_process_metadata(decoder)) {
			cleanup();
			return 0;
		}
	}

	if(needs_seek) {
		FLAC__seekable_stream_decoder_seek_absolute(decoder, seek_sample);
		needs_seek = false;
	}

	bool eof = false;

	while(samples_in_reservoir < 576) {
		if(FLAC__seekable_stream_decoder_get_state(decoder) == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) {
			eof = true;
			break;
		}
		else if(!FLAC__seekable_stream_decoder_process_one_frame(decoder))
			break;
	}

	if(samples_in_reservoir == 0) {
		eof = true;
	}
	else {
		const unsigned channels = stream_info.channels;
		const unsigned bits_per_sample = stream_info.bits_per_sample;
		const unsigned bytes_per_sample = (bits_per_sample+7)/8;
		const unsigned sample_rate = stream_info.sample_rate;
		unsigned i, n = min(samples_in_reservoir, 576), delta;
		signed short *ssbuffer = (signed short*)output;

		for(i = 0; i < n*channels; i++)
			ssbuffer[i] = reservoir[i];
		delta = i;
		for( ; i < samples_in_reservoir*channels; i++)
			reservoir[i-delta] = reservoir[i];
		samples_in_reservoir -= n;

		const int bytes = n * channels * bytes_per_sample;

		ChunkInfosI *ci=new ChunkInfosI();
		ci->addInfo("srate", sample_rate);
		ci->addInfo("bps", bits_per_sample);
		ci->addInfo("nch", channels);

		chunk_list->setChunk("PCM", output, bytes, ci);
	}

	if(eof)
		return 0;

	return 1;
}

int FlacPcm::corecb_onSeeked(int newpos)
{
	if(stream_info.total_samples == 0 || newpos < 0)
		return 1;

	needs_seek = true;
	seek_sample = (FLAC__uint64)((double)newpos / (double)lengthInMsec() * (double)(FLAC__int64)stream_info.total_samples);
	return 0;
}

void FlacPcm::cleanup()
{
	if(decoder) {
		FLAC__seekable_stream_decoder_finish(decoder);
		FLAC__seekable_stream_decoder_delete(decoder);
		decoder = 0;
	}
}
FLAC__SeekableStreamDecoderReadStatus FlacPcm::readCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	*bytes = instance->reader->read((char*)buffer, *bytes);
	return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
}

FLAC__SeekableStreamDecoderSeekStatus FlacPcm::seekCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	if(instance->reader->seek((int)absolute_byte_offset) < 0)
		return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
	else
		return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
}

FLAC__SeekableStreamDecoderTellStatus FlacPcm::tellCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	int pos = instance->reader->getPos();
	if(pos < 0)
		return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
	else {
		*absolute_byte_offset = pos;
		return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
	}
}

FLAC__SeekableStreamDecoderLengthStatus FlacPcm::lengthCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	int length = instance->reader->getPos();
	if(length < 0)
		return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;
	else {
		*stream_length = length;
		return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
	}
}

FLAC__bool FlacPcm::eofCallback_(const FLAC__SeekableStreamDecoder *decoder, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	return instance->reader->getPos() == instance->reader->getLength();
}

FLAC__StreamDecoderWriteStatus FlacPcm::writeCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	const unsigned bps = instance->stream_info.bits_per_sample, channels = instance->stream_info.channels, wide_samples = frame->header.blocksize;
	unsigned wide_sample, sample, channel;

	(void)decoder;

	if(instance->abort_flag)
		return FLAC__STREAM_DECODER_WRITE_ABORT;

	for(sample = instance->samples_in_reservoir*channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++)
		for(channel = 0; channel < channels; channel++, sample++)
			instance->reservoir[sample] = (FLAC__int16)buffer[channel][wide_sample];

	instance->samples_in_reservoir += wide_samples;

	return FLAC__STREAM_DECODER_WRITE_CONTINUE;
}

void FlacPcm::metadataCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	(void)decoder;
	if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
		instance->stream_info = metadata->data.stream_info;

		if(instance->stream_info.bits_per_sample != 16) {
			//@@@ how to do this?  MessageBox(mod.hMainWindow, "ERROR: plugin can only handle 16-bit samples\n", "ERROR: plugin can only handle 16-bit samples", 0);
			instance->abort_flag = true;
			return;
		}
	}
}

void FlacPcm::errorCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	(void)decoder;
	if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC)
		instance->abort_flag = true;
}

/***********************************************************************
 * local routines
 **********************************************************************/

bool get_id3v1_tag_(const char *filename, id3v1_struct *tag)
{
	const char *temp;
	FILE *f = fopen(filename, "rb");
	memset(tag, 0, sizeof(id3v1_struct));
strcpy(tag->title,"Sonata K.42");
strcpy(tag->artist,"Domenico Scarlatti");
strcpy(tag->album,"Narcisso Yepes Plays Scarlatti");
sprintf(tag->description, "%s - %s", tag->artist, tag->title);

	/* set the title and description to the filename by default */
	temp = strrchr(filename, '/');
	if(!temp)
		temp = filename;
	else
		temp++;
	strcpy(tag->description, temp);
	*strrchr(tag->description, '.') = '\0';
	strncpy(tag->title, tag->description, 30); tag->title[30] = '\0';

	if(0 == f)
		return false;
	if(-1 == fseek(f, -128, SEEK_END)) {
		fclose(f);
		return false;
	}
	if(fread(tag->raw, 1, 128, f) < 128) {
		fclose(f);
		return false;
	}
	fclose(f);
	if(strncmp((const char*)tag->raw, "TAG", 3))
		return false;
	else {
		char year_str[5];

		memcpy(tag->title, tag->raw+3, 30);
		memcpy(tag->artist, tag->raw+33, 30);
		memcpy(tag->album, tag->raw+63, 30);
		memcpy(year_str, tag->raw+93, 4); year_str[4] = '\0'; tag->year = atoi(year_str);
		memcpy(tag->comment, tag->raw+97, 30);
		tag->genre = (unsigned)((FLAC__byte)tag->raw[127]);
		tag->track = (unsigned)((FLAC__byte)tag->raw[126]);

		sprintf(tag->description, "%s - %s", tag->artist, tag->title);

		return true;
	}
}
<commit_msg>update to use new metadata interface<commit_after>/* FLAC input plugin for Winamp3
 * Copyright (C) 2000,2001,2002  Josh Coalson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * NOTE: this code is derived from the 'rawpcm' example by
 * Nullsoft; the original license for the 'rawpcm' example follows.
 */
/*

  Nullsoft WASABI Source File License

  Copyright 1999-2001 Nullsoft, Inc.

    This software is provided 'as-is', without any express or implied
    warranty.  In no event will the authors be held liable for any damages
    arising from the use of this software.

    Permission is granted to anyone to use this software for any purpose,
    including commercial applications, and to alter it and redistribute it
    freely, subject to the following restrictions:

    1. The origin of this software must not be misrepresented; you must not
       claim that you wrote the original software. If you use this software
       in a product, an acknowledgment in the product documentation would be
       appreciated but is not required.
    2. Altered source versions must be plainly marked as such, and must not be
       misrepresented as being the original software.
    3. This notice may not be removed or altered from any source distribution.

  Brennan Underwood
  brennan@nullsoft.com

*/

#include "flacpcm.h"
extern "C" {
#include "FLAC/metadata.h"
};

struct id3v1_struct {
	FLAC__byte raw[128];
	char title[31];
	char artist[31];
	char album[31];
	char comment[31];
	unsigned year;
	unsigned track; /* may be 0 if v1 (not v1.1) tag */
	unsigned genre;
	char description[1024]; /* the formatted description passed to the gui */
};

static bool get_id3v1_tag_(const char *filename, id3v1_struct *tag);

FlacPcm::FlacPcm():
needs_seek(false),
seek_sample(0),
samples_in_reservoir(0),
abort_flag(false),
reader(0),
decoder(0)
{
}

FlacPcm::~FlacPcm()
{
	cleanup();
}

int FlacPcm::getInfos(MediaInfo *infos)
{
	reader = infos->getReader();
	if(!reader) return 0;

	//@@@ to be really "clean" we should go through the reader instead of directly to the file...
	if(!FLAC__metadata_get_streaminfo(infos->getFilename(), &stream_info))
		return 1;

	id3v1_struct tag;

	//@@@ ditto here...
	bool has_tag = get_id3v1_tag_(infos->getFilename(), &tag);

	infos->setLength(lengthInMsec());
	//@@@ infos->setTitle(Std::filename(infos->getFilename()));
	infos->setTitle(tag.description);
	infos->setInfo(StringPrintf("FLAC:<%ihz:%ibps:%dch>", stream_info.sample_rate, stream_info.bits_per_sample, stream_info.channels)); //@@@ fix later
	if(has_tag) {
		infos->setData("Title", tag.title);
		infos->setData("Artist", tag.artist);
		infos->setData("Album", tag.album);
	}

	return 0;
}

int FlacPcm::processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch)
{
	reader = infos->getReader();
	if(reader == 0)
		return 0;

	if(decoder == 0) {
		decoder = FLAC__seekable_stream_decoder_new();
		if(decoder == 0)
			return 0;
		FLAC__seekable_stream_decoder_set_md5_checking(decoder, false);
		FLAC__seekable_stream_decoder_set_read_callback(decoder, readCallback_);
		FLAC__seekable_stream_decoder_set_seek_callback(decoder, seekCallback_);
		FLAC__seekable_stream_decoder_set_tell_callback(decoder, tellCallback_);
		FLAC__seekable_stream_decoder_set_length_callback(decoder, lengthCallback_);
		FLAC__seekable_stream_decoder_set_eof_callback(decoder, eofCallback_);
		FLAC__seekable_stream_decoder_set_write_callback(decoder, writeCallback_);
		FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadataCallback_);
		FLAC__seekable_stream_decoder_set_error_callback(decoder, errorCallback_);
		FLAC__seekable_stream_decoder_set_client_data(decoder, this);

		if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) {
			cleanup();
			return 0;
		}
		if(!FLAC__seekable_stream_decoder_process_metadata(decoder)) {
			cleanup();
			return 0;
		}
	}

	if(needs_seek) {
		FLAC__seekable_stream_decoder_seek_absolute(decoder, seek_sample);
		needs_seek = false;
	}

	bool eof = false;

	while(samples_in_reservoir < 576) {
		if(FLAC__seekable_stream_decoder_get_state(decoder) == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) {
			eof = true;
			break;
		}
		else if(!FLAC__seekable_stream_decoder_process_one_frame(decoder))
			break;
	}

	if(samples_in_reservoir == 0) {
		eof = true;
	}
	else {
		const unsigned channels = stream_info.channels;
		const unsigned bits_per_sample = stream_info.bits_per_sample;
		const unsigned bytes_per_sample = (bits_per_sample+7)/8;
		const unsigned sample_rate = stream_info.sample_rate;
		unsigned i, n = min(samples_in_reservoir, 576), delta;
		signed short *ssbuffer = (signed short*)output;

		for(i = 0; i < n*channels; i++)
			ssbuffer[i] = reservoir[i];
		delta = i;
		for( ; i < samples_in_reservoir*channels; i++)
			reservoir[i-delta] = reservoir[i];
		samples_in_reservoir -= n;

		const int bytes = n * channels * bytes_per_sample;

		ChunkInfosI *ci=new ChunkInfosI();
		ci->addInfo("srate", sample_rate);
		ci->addInfo("bps", bits_per_sample);
		ci->addInfo("nch", channels);

		chunk_list->setChunk("PCM", output, bytes, ci);
	}

	if(eof)
		return 0;

	return 1;
}

int FlacPcm::corecb_onSeeked(int newpos)
{
	if(stream_info.total_samples == 0 || newpos < 0)
		return 1;

	needs_seek = true;
	seek_sample = (FLAC__uint64)((double)newpos / (double)lengthInMsec() * (double)(FLAC__int64)stream_info.total_samples);
	return 0;
}

void FlacPcm::cleanup()
{
	if(decoder) {
		FLAC__seekable_stream_decoder_finish(decoder);
		FLAC__seekable_stream_decoder_delete(decoder);
		decoder = 0;
	}
}
FLAC__SeekableStreamDecoderReadStatus FlacPcm::readCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	*bytes = instance->reader->read((char*)buffer, *bytes);
	return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
}

FLAC__SeekableStreamDecoderSeekStatus FlacPcm::seekCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	if(instance->reader->seek((int)absolute_byte_offset) < 0)
		return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
	else
		return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
}

FLAC__SeekableStreamDecoderTellStatus FlacPcm::tellCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	int pos = instance->reader->getPos();
	if(pos < 0)
		return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
	else {
		*absolute_byte_offset = pos;
		return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
	}
}

FLAC__SeekableStreamDecoderLengthStatus FlacPcm::lengthCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	int length = instance->reader->getPos();
	if(length < 0)
		return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;
	else {
		*stream_length = length;
		return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
	}
}

FLAC__bool FlacPcm::eofCallback_(const FLAC__SeekableStreamDecoder *decoder, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	return instance->reader->getPos() == instance->reader->getLength();
}

FLAC__StreamDecoderWriteStatus FlacPcm::writeCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	const unsigned bps = instance->stream_info.bits_per_sample, channels = instance->stream_info.channels, wide_samples = frame->header.blocksize;
	unsigned wide_sample, sample, channel;

	(void)decoder;

	if(instance->abort_flag)
		return FLAC__STREAM_DECODER_WRITE_ABORT;

	for(sample = instance->samples_in_reservoir*channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++)
		for(channel = 0; channel < channels; channel++, sample++)
			instance->reservoir[sample] = (FLAC__int16)buffer[channel][wide_sample];

	instance->samples_in_reservoir += wide_samples;

	return FLAC__STREAM_DECODER_WRITE_CONTINUE;
}

void FlacPcm::metadataCallback_(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	(void)decoder;
	if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
		instance->stream_info = metadata->data.stream_info;

		if(instance->stream_info.bits_per_sample != 16) {
			//@@@ how to do this?  MessageBox(mod.hMainWindow, "ERROR: plugin can only handle 16-bit samples\n", "ERROR: plugin can only handle 16-bit samples", 0);
			instance->abort_flag = true;
			return;
		}
	}
}

void FlacPcm::errorCallback_(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
	FlacPcm *instance = (FlacPcm*)client_data;
	(void)decoder;
	if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC)
		instance->abort_flag = true;
}

/***********************************************************************
 * local routines
 **********************************************************************/

bool get_id3v1_tag_(const char *filename, id3v1_struct *tag)
{
	const char *temp;
	FILE *f = fopen(filename, "rb");
	memset(tag, 0, sizeof(id3v1_struct));
strcpy(tag->title,"Sonata K.42");
strcpy(tag->artist,"Domenico Scarlatti");
strcpy(tag->album,"Narcisso Yepes Plays Scarlatti");
sprintf(tag->description, "%s - %s", tag->artist, tag->title);

	/* set the title and description to the filename by default */
	temp = strrchr(filename, '/');
	if(!temp)
		temp = filename;
	else
		temp++;
	strcpy(tag->description, temp);
	*strrchr(tag->description, '.') = '\0';
	strncpy(tag->title, tag->description, 30); tag->title[30] = '\0';

	if(0 == f)
		return false;
	if(-1 == fseek(f, -128, SEEK_END)) {
		fclose(f);
		return false;
	}
	if(fread(tag->raw, 1, 128, f) < 128) {
		fclose(f);
		return false;
	}
	fclose(f);
	if(strncmp((const char*)tag->raw, "TAG", 3))
		return false;
	else {
		char year_str[5];

		memcpy(tag->title, tag->raw+3, 30);
		memcpy(tag->artist, tag->raw+33, 30);
		memcpy(tag->album, tag->raw+63, 30);
		memcpy(year_str, tag->raw+93, 4); year_str[4] = '\0'; tag->year = atoi(year_str);
		memcpy(tag->comment, tag->raw+97, 30);
		tag->genre = (unsigned)((FLAC__byte)tag->raw[127]);
		tag->track = (unsigned)((FLAC__byte)tag->raw[126]);

		sprintf(tag->description, "%s - %s", tag->artist, tag->title);

		return true;
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the WebSocket++ Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
#define WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP

#include "processor.hpp"

#include "../md5/md5.hpp"
#include "../network_utilities.hpp"

namespace websocketpp {
namespace processor {

namespace hybi_legacy_state {
    enum value {
        INIT = 0,
        READ = 1,
        DONE = 2
    };
}

template <class connection_type>
class hybi_legacy : public processor_base {
public:
    hybi_legacy(connection_type &connection)
     : m_connection(connection),
       m_state(hybi_legacy_state::INIT)
    {
        reset();
    }

    void validate_handshake(const http::parser::request& headers) const {

    }

    void handshake_response(const http::parser::request& request,http::parser::response& response) {
        char key_final[16];

        // copy key1 into final key
        *reinterpret_cast<uint32_t*>(&key_final[0]) =
            decode_client_key(request.header("Sec-WebSocket-Key1"));

        // copy key2 into final key
        *reinterpret_cast<uint32_t*>(&key_final[4]) =
            decode_client_key(request.header("Sec-WebSocket-Key2"));

        // copy key3 into final key
        memcpy(&key_final[8],request.header("Sec-WebSocket-Key3").c_str(),8);

        m_key3 = md5_hash_string(std::string(key_final,16));

        response.add_header("Upgrade","websocket");
        response.add_header("Connection","Upgrade");

        // TODO: require headers that need application specific information?

        // Echo back client's origin unless our local application set a
        // more restrictive one.
        if (response.header("Sec-WebSocket-Origin") == "") {
            response.add_header("Sec-WebSocket-Origin",request.header("Origin"));
        }

        // Echo back the client's request host unless our local application
        // set a different one.
        if (response.header("Sec-WebSocket-Location") == "") {
            // TODO: extract from host header rather than hard code
            uri_ptr uri = get_uri(request);
            response.add_header("Sec-WebSocket-Location",uri->str());
        }
    }

    std::string get_origin(const http::parser::request& request) const {
        return request.header("Origin");
    }

    uri_ptr get_uri(const http::parser::request& request) const {
        std::string h = request.header("Host");

        size_t last_colon = h.rfind(":");
        size_t last_sbrace = h.rfind("]");

        // no : = hostname with no port
        // last : before ] = ipv6 literal with no port
        // : with no ] = hostname with port
        // : after ] = ipv6 literal with port
        if (last_colon == std::string::npos ||
            (last_sbrace != std::string::npos && last_sbrace > last_colon))
        {
            return uri_ptr(new uri(m_connection.is_secure(),h,request.uri()));
        } else {
            return uri_ptr(new uri(m_connection.is_secure(),
                                   h.substr(0,last_colon),
                                   h.substr(last_colon+1),
                                   request.uri()));
        }

        // TODO: check if get_uri is a full uri
    }

    void consume(std::istream& s) {
        //unsigned char c;
        while (s.good() && m_state != hybi_legacy_state::DONE) {
            //c = s.get();
            //if (s.good()) {
                process(s);
            //}
        }
    }

    bool ready() const {
        return m_state == hybi_legacy_state::DONE;
    }

    // legacy hybi has no control messages.
    bool is_control() const {
        return false;
    }

    message::data_ptr get_data_message() {
        message::data_ptr p = m_data_message;
        m_data_message.reset();
        return p;
    }

    message::control_ptr get_control_message() {
        throw "Hybi legacy has no control messages.";
    }

    void process(std::istream& input) {
        if (m_state == hybi_legacy_state::INIT) {
            // we are looking for a 0x00
            if (input.peek() == 0x00) {
                // start a message
                input.ignore();

                m_state = hybi_legacy_state::READ;

                m_data_message = m_connection.get_data_message();

                if (!m_data_message) {
                    throw processor::exception("Out of data messages",processor::error::OUT_OF_MESSAGES);
                }

                m_data_message->reset(frame::opcode::TEXT);
            } else {
                input.ignore();
                // TODO: ignore or error
                //std::stringstream foo;

                //foo << "invalid character read: |" << input.peek() << "|";

                std::cout << "invalid character read: |" << input.peek() << "|" << std::endl;

                //throw processor::exception(foo.str(),processor::error::PROTOCOL_VIOLATION);
            }
        } else if (m_state == hybi_legacy_state::READ) {
            if (input.peek() == 0xFF) {
                // end
                input.ignore();

                m_state = hybi_legacy_state::DONE;
            } else {
                if (m_data_message) {
                    m_data_message->process_payload(input,1);
                }
            }
        }
    }

    void reset() {
        m_state = hybi_legacy_state::INIT;
        m_data_message.reset();
    }

    uint64_t get_bytes_needed() const {
        return 1;
    }

    std::string get_key3() const {
        return m_key3;
    }

    // TODO: to factor away
    binary_string_ptr prepare_frame(frame::opcode::value opcode,
                                    bool mask,
                                    const binary_string& payload)  {
        if (opcode != frame::opcode::TEXT) {
            // TODO: hybi_legacy doesn't allow non-text frames.
            throw;
        }

        // TODO: mask = ignore?

        // TODO: utf8 validation on payload.

        binary_string_ptr response(new binary_string(payload.size()+2));

        (*response)[0] = 0x00;
        std::copy(payload.begin(),payload.end(),response->begin()+1);
        (*response)[response->size()-1] = 0xFF;

        return response;
    }

    binary_string_ptr prepare_frame(frame::opcode::value opcode,
                                    bool mask,
                                    const utf8_string& payload)  {
        if (opcode != frame::opcode::TEXT) {
            // TODO: hybi_legacy doesn't allow non-text frames.
            throw;
        }

        // TODO: mask = ignore?

        // TODO: utf8 validation on payload.

        binary_string_ptr response(new binary_string(payload.size()+2));

        (*response)[0] = 0x00;
        std::copy(payload.begin(),payload.end(),response->begin()+1);
        (*response)[response->size()-1] = 0xFF;

        return response;
    }

    binary_string_ptr prepare_close_frame(close::status::value code,
                                          bool mask,
                                          const std::string& reason) {
        binary_string_ptr response(new binary_string(2));

        (*response)[0] = 0xFF;
        (*response)[1] = 0x00;

        return response;
    }

    void prepare_frame(message::data_ptr msg) {
        assert(msg);
        if (msg->get_prepared()) {
            return;
        }

        msg->set_header(std::string(0x00));

        msg->append_payload(std::string(1,0xFF));

        msg->set_prepared(true);
    }

    void prepare_close_frame(message::data_ptr msg,
                             close::status::value code,
                             const std::string& reason)
    {
        assert(msg);
        if (msg->get_prepared()) {
            return;
        }

        msg->set_header(std::string());

        std::string val;
        val.append(1,0xFF);
        val.append(1,0x00);
        msg->set_payload(val);

        msg->set_prepared(true);
    }

private:
    uint32_t decode_client_key(const std::string& key) {
        int spaces = 0;
        std::string digits = "";
        uint32_t num;

        // key2
        for (size_t i = 0; i < key.size(); i++) {
            if (key[i] == ' ') {
                spaces++;
            } else if (key[i] >= '0' && key[i] <= '9') {
                digits += key[i];
            }
        }

        num = atoi(digits.c_str());
        if (spaces > 0 && num > 0) {
            return htonl(num/spaces);
        } else {
            return 0;
        }
    }

    connection_type&            m_connection;
    hybi_legacy_state::value    m_state;

    message::data_ptr           m_data_message;

    std::string                 m_key3;
};

} // processor
} // websocketpp
#endif // WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
<commit_msg>fixes a crash when sending messages to hybi00 clients<commit_after>/*
 * Copyright (c) 2011, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the WebSocket++ Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
#define WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP

#include "processor.hpp"

#include "../md5/md5.hpp"
#include "../network_utilities.hpp"

namespace websocketpp {
namespace processor {

namespace hybi_legacy_state {
    enum value {
        INIT = 0,
        READ = 1,
        DONE = 2
    };
}

template <class connection_type>
class hybi_legacy : public processor_base {
public:
    hybi_legacy(connection_type &connection)
     : m_connection(connection),
       m_state(hybi_legacy_state::INIT)
    {
        reset();
    }

    void validate_handshake(const http::parser::request& headers) const {

    }

    void handshake_response(const http::parser::request& request,http::parser::response& response) {
        char key_final[16];

        // copy key1 into final key
        *reinterpret_cast<uint32_t*>(&key_final[0]) =
            decode_client_key(request.header("Sec-WebSocket-Key1"));

        // copy key2 into final key
        *reinterpret_cast<uint32_t*>(&key_final[4]) =
            decode_client_key(request.header("Sec-WebSocket-Key2"));

        // copy key3 into final key
        memcpy(&key_final[8],request.header("Sec-WebSocket-Key3").c_str(),8);

        m_key3 = md5_hash_string(std::string(key_final,16));

        response.add_header("Upgrade","websocket");
        response.add_header("Connection","Upgrade");

        // TODO: require headers that need application specific information?

        // Echo back client's origin unless our local application set a
        // more restrictive one.
        if (response.header("Sec-WebSocket-Origin") == "") {
            response.add_header("Sec-WebSocket-Origin",request.header("Origin"));
        }

        // Echo back the client's request host unless our local application
        // set a different one.
        if (response.header("Sec-WebSocket-Location") == "") {
            // TODO: extract from host header rather than hard code
            uri_ptr uri = get_uri(request);
            response.add_header("Sec-WebSocket-Location",uri->str());
        }
    }

    std::string get_origin(const http::parser::request& request) const {
        return request.header("Origin");
    }

    uri_ptr get_uri(const http::parser::request& request) const {
        std::string h = request.header("Host");

        size_t last_colon = h.rfind(":");
        size_t last_sbrace = h.rfind("]");

        // no : = hostname with no port
        // last : before ] = ipv6 literal with no port
        // : with no ] = hostname with port
        // : after ] = ipv6 literal with port
        if (last_colon == std::string::npos ||
            (last_sbrace != std::string::npos && last_sbrace > last_colon))
        {
            return uri_ptr(new uri(m_connection.is_secure(),h,request.uri()));
        } else {
            return uri_ptr(new uri(m_connection.is_secure(),
                                   h.substr(0,last_colon),
                                   h.substr(last_colon+1),
                                   request.uri()));
        }

        // TODO: check if get_uri is a full uri
    }

    void consume(std::istream& s) {
        //unsigned char c;
        while (s.good() && m_state != hybi_legacy_state::DONE) {
            //c = s.get();
            //if (s.good()) {
                process(s);
            //}
        }
    }

    bool ready() const {
        return m_state == hybi_legacy_state::DONE;
    }

    // legacy hybi has no control messages.
    bool is_control() const {
        return false;
    }

    message::data_ptr get_data_message() {
        message::data_ptr p = m_data_message;
        m_data_message.reset();
        return p;
    }

    message::control_ptr get_control_message() {
        throw "Hybi legacy has no control messages.";
    }

    void process(std::istream& input) {
        if (m_state == hybi_legacy_state::INIT) {
            // we are looking for a 0x00
            if (input.peek() == 0x00) {
                // start a message
                input.ignore();

                m_state = hybi_legacy_state::READ;

                m_data_message = m_connection.get_data_message();

                if (!m_data_message) {
                    throw processor::exception("Out of data messages",processor::error::OUT_OF_MESSAGES);
                }

                m_data_message->reset(frame::opcode::TEXT);
            } else {
                input.ignore();
                // TODO: ignore or error
                //std::stringstream foo;

                //foo << "invalid character read: |" << input.peek() << "|";

                std::cout << "invalid character read: |" << input.peek() << "|" << std::endl;

                //throw processor::exception(foo.str(),processor::error::PROTOCOL_VIOLATION);
            }
        } else if (m_state == hybi_legacy_state::READ) {
            if (input.peek() == 0xFF) {
                // end
                input.ignore();

                m_state = hybi_legacy_state::DONE;
            } else {
                if (m_data_message) {
                    m_data_message->process_payload(input,1);
                }
            }
        }
    }

    void reset() {
        m_state = hybi_legacy_state::INIT;
        m_data_message.reset();
    }

    uint64_t get_bytes_needed() const {
        return 1;
    }

    std::string get_key3() const {
        return m_key3;
    }

    // TODO: to factor away
    binary_string_ptr prepare_frame(frame::opcode::value opcode,
                                    bool mask,
                                    const binary_string& payload)  {
        if (opcode != frame::opcode::TEXT) {
            // TODO: hybi_legacy doesn't allow non-text frames.
            throw;
        }

        // TODO: mask = ignore?

        // TODO: utf8 validation on payload.

        binary_string_ptr response(new binary_string(payload.size()+2));

        (*response)[0] = 0x00;
        std::copy(payload.begin(),payload.end(),response->begin()+1);
        (*response)[response->size()-1] = 0xFF;

        return response;
    }

    binary_string_ptr prepare_frame(frame::opcode::value opcode,
                                    bool mask,
                                    const utf8_string& payload)  {
        if (opcode != frame::opcode::TEXT) {
            // TODO: hybi_legacy doesn't allow non-text frames.
            throw;
        }

        // TODO: mask = ignore?

        // TODO: utf8 validation on payload.

        binary_string_ptr response(new binary_string(payload.size()+2));

        (*response)[0] = 0x00;
        std::copy(payload.begin(),payload.end(),response->begin()+1);
        (*response)[response->size()-1] = 0xFF;

        return response;
    }

    binary_string_ptr prepare_close_frame(close::status::value code,
                                          bool mask,
                                          const std::string& reason) {
        binary_string_ptr response(new binary_string(2));

        (*response)[0] = 0xFF;
        (*response)[1] = 0x00;

        return response;
    }

    void prepare_frame(message::data_ptr msg) {
        assert(msg);
        if (msg->get_prepared()) {
            return;
        }

        msg->set_header(std::string(1,0x00));

        msg->append_payload(std::string(1,0xFF));

        msg->set_prepared(true);
    }

    void prepare_close_frame(message::data_ptr msg,
                             close::status::value code,
                             const std::string& reason)
    {
        assert(msg);
        if (msg->get_prepared()) {
            return;
        }

        msg->set_header(std::string());

        std::string val;
        val.append(1,0xFF);
        val.append(1,0x00);
        msg->set_payload(val);

        msg->set_prepared(true);
    }

private:
    uint32_t decode_client_key(const std::string& key) {
        int spaces = 0;
        std::string digits = "";
        uint32_t num;

        // key2
        for (size_t i = 0; i < key.size(); i++) {
            if (key[i] == ' ') {
                spaces++;
            } else if (key[i] >= '0' && key[i] <= '9') {
                digits += key[i];
            }
        }

        num = atoi(digits.c_str());
        if (spaces > 0 && num > 0) {
            return htonl(num/spaces);
        } else {
            return 0;
        }
    }

    connection_type&            m_connection;
    hybi_legacy_state::value    m_state;

    message::data_ptr           m_data_message;

    std::string                 m_key3;
};

} // processor
} // websocketpp
#endif // WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/profiling/memory/client.h"

#include <inttypes.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

#include <atomic>
#include <new>

#include <unwindstack/MachineArm.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/MachineMips.h>
#include <unwindstack/MachineMips64.h>
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>

#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/thread_utils.h"
#include "perfetto/base/unix_socket.h"
#include "perfetto/base/utils.h"
#include "src/profiling/memory/sampler.h"
#include "src/profiling/memory/scoped_spinlock.h"
#include "src/profiling/memory/wire_protocol.h"

namespace perfetto {
namespace profiling {
namespace {

const char kSingleByte[1] = {'x'};
constexpr std::chrono::seconds kLockTimeout{1};

base::Optional<base::UnixSocketRaw> Connect(const std::string& sock_name) {
  auto sock = base::UnixSocketRaw::CreateMayFail(base::SockType::kStream);
  if (!sock || !sock.Connect(sock_name)) {
    PERFETTO_PLOG("Failed to connect to %s", sock_name.c_str());
    return base::nullopt;
  }
  if (!sock.SetTxTimeout(kClientSockTimeoutMs)) {
    PERFETTO_PLOG("Failed to set send timeout for %s", sock_name.c_str());
    return base::nullopt;
  }
  if (!sock.SetRxTimeout(kClientSockTimeoutMs)) {
    PERFETTO_PLOG("Failed to set receive timeout for %s", sock_name.c_str());
    return base::nullopt;
  }
  return std::move(sock);
}

inline bool IsMainThread() {
  return getpid() == base::GetThreadId();
}

// TODO(b/117203899): Remove this after making bionic implementation safe to
// use.
char* FindMainThreadStack() {
  base::ScopedFstream maps(fopen("/proc/self/maps", "r"));
  if (!maps) {
    return nullptr;
  }
  while (!feof(*maps)) {
    char line[1024];
    char* data = fgets(line, sizeof(line), *maps);
    if (data != nullptr && strstr(data, "[stack]")) {
      char* sep = strstr(data, "-");
      if (sep == nullptr)
        continue;
      sep++;
      return reinterpret_cast<char*>(strtoll(sep, nullptr, 16));
    }
  }
  return nullptr;
}

int UnsetDumpable(int) {
  prctl(PR_SET_DUMPABLE, 0);
  return 0;
}

}  // namespace

bool FreePage::Add(const uint64_t addr,
                   const uint64_t sequence_number,
                   Client* client) {
  std::unique_lock<std::timed_mutex> l(mutex_, kLockTimeout);
  if (!l.owns_lock())
    return false;
  if (free_page_.num_entries == kFreePageSize) {
    if (!client->FlushFrees(&free_page_))
      return false;
    // Now that we have flushed, reset to after the header.
    free_page_.num_entries = 0;
  }
  FreePageEntry& current_entry = free_page_.entries[free_page_.num_entries++];
  current_entry.sequence_number = sequence_number;
  current_entry.addr = addr;
  return true;
}

bool Client::FlushFrees(FreeMetadata* free_metadata) {
  WireMessage msg = {};
  msg.record_type = RecordType::Free;
  msg.free_header = free_metadata;
  if (!SendWireMessage(&shmem_, msg)) {
    PERFETTO_PLOG("Failed to send wire message");
    Shutdown();
    return false;
  }
  if (!sock_.Send(kSingleByte, sizeof(kSingleByte))) {
    Shutdown();
    return false;
  }
  return true;
}

const char* GetThreadStackBase() {
  pthread_attr_t attr;
  if (pthread_getattr_np(pthread_self(), &attr) != 0)
    return nullptr;
  base::ScopedResource<pthread_attr_t*, pthread_attr_destroy, nullptr> cleanup(
      &attr);

  char* stackaddr;
  size_t stacksize;
  if (pthread_attr_getstack(&attr, reinterpret_cast<void**>(&stackaddr),
                            &stacksize) != 0)
    return nullptr;
  return stackaddr + stacksize;
}

Client::Client(base::Optional<base::UnixSocketRaw> sock)
    : sampler_(8192),  // placeholder until we receive the config (within ctor)
      main_thread_stack_base_(FindMainThreadStack()) {
  if (!sock || !sock.value()) {
    PERFETTO_DFATAL("Socket not connected.");
    return;
  }
  sock_ = std::move(sock.value());

  // We might be running in a process that is not dumpable (such as app
  // processes on user builds), in which case the /proc/self/mem will be chown'd
  // to root:root, and will not be accessible even to the process itself (see
  // man 5 proc). In such situations, temporarily mark the process dumpable to
  // be able to open the files, unsetting dumpability immediately afterwards.
  int orig_dumpable = prctl(PR_GET_DUMPABLE);

  enum { kNop, kDoUnset };
  base::ScopedResource<int, UnsetDumpable, kNop, false> unset_dumpable(kNop);
  if (orig_dumpable == 0) {
    unset_dumpable.reset(kDoUnset);
    prctl(PR_SET_DUMPABLE, 1);
  }

  base::ScopedFile maps(base::OpenFile("/proc/self/maps", O_RDONLY));
  if (!maps) {
    PERFETTO_DFATAL("Failed to open /proc/self/maps");
    return;
  }
  base::ScopedFile mem(base::OpenFile("/proc/self/mem", O_RDONLY));
  if (!mem) {
    PERFETTO_DFATAL("Failed to open /proc/self/mem");
    return;
  }
  // Restore original dumpability value if we overrode it.
  unset_dumpable.reset();

  int fds[kHandshakeSize];
  fds[kHandshakeMaps] = *maps;
  fds[kHandshakeMem] = *mem;

  // Send an empty record to transfer fds for /proc/self/maps and
  // /proc/self/mem.
  if (sock_.Send(kSingleByte, sizeof(kSingleByte), fds, kHandshakeSize) !=
      sizeof(kSingleByte)) {
    PERFETTO_DFATAL("Failed to send file descriptors.");
    return;
  }

  base::ScopedFile shmem_fd;
  if (sock_.Receive(&client_config_, sizeof(client_config_), &shmem_fd, 1) !=
      sizeof(client_config_)) {
    PERFETTO_DFATAL("Failed to receive client config.");
    return;
  }

  auto shmem = SharedRingBuffer::Attach(std::move(shmem_fd));
  if (!shmem || !shmem->is_valid()) {
    PERFETTO_DFATAL("Failed to attach to shmem.");
    return;
  }
  shmem_ = std::move(shmem.value());

  PERFETTO_DCHECK(client_config_.interval >= 1);
  sampler_ = Sampler(client_config_.interval);

  PERFETTO_DLOG("Initialized client.");
  inited_.store(true, std::memory_order_release);
}

Client::Client(const std::string& sock_name) : Client(Connect(sock_name)) {}

const char* Client::GetStackBase() {
  if (IsMainThread()) {
    if (!main_thread_stack_base_)
      // Because pthread_attr_getstack reads and parses /proc/self/maps and
      // /proc/self/stat, we have to cache the result here.
      main_thread_stack_base_ = GetThreadStackBase();
    return main_thread_stack_base_;
  }
  return GetThreadStackBase();
}

// The stack grows towards numerically smaller addresses, so the stack layout
// of main calling malloc is as follows.
//
//               +------------+
//               |SendWireMsg |
// stacktop +--> +------------+ 0x1000
//               |RecordMalloc|    +
//               +------------+    |
//               | malloc     |    |
//               +------------+    |
//               |  main      |    v
// stackbase +-> +------------+ 0xffff
bool Client::RecordMalloc(uint64_t alloc_size,
                          uint64_t total_size,
                          uint64_t alloc_address) {
  if (!inited_.load(std::memory_order_acquire)) {
    return false;
  }
  AllocMetadata metadata;
  const char* stackbase = GetStackBase();
  const char* stacktop = reinterpret_cast<char*>(__builtin_frame_address(0));
  unwindstack::AsmGetRegs(metadata.register_data);

  if (stackbase < stacktop) {
    PERFETTO_DFATAL("Stackbase >= stacktop.");
    Shutdown();
    return false;
  }

  uint64_t stack_size = static_cast<uint64_t>(stackbase - stacktop);
  metadata.total_size = total_size;
  metadata.alloc_size = alloc_size;
  metadata.alloc_address = alloc_address;
  metadata.stack_pointer = reinterpret_cast<uint64_t>(stacktop);
  metadata.stack_pointer_offset = sizeof(AllocMetadata);
  metadata.arch = unwindstack::Regs::CurrentArch();
  metadata.sequence_number =
      1 + sequence_number_.fetch_add(1, std::memory_order_acq_rel);

  WireMessage msg{};
  msg.record_type = RecordType::Malloc;
  msg.alloc_header = &metadata;
  msg.payload = const_cast<char*>(stacktop);
  msg.payload_size = static_cast<size_t>(stack_size);

  if (!SendWireMessage(&shmem_, msg)) {
    PERFETTO_PLOG("Failed to send wire message.");
    Shutdown();
    return false;
  }
  if (sock_.Send(kSingleByte, sizeof(kSingleByte)) == -1) {
    PERFETTO_PLOG("Failed to send wire message.");
    Shutdown();
    return false;
  }
  return true;
}

bool Client::RecordFree(uint64_t alloc_address) {
  if (!inited_.load(std::memory_order_acquire))
    return false;
  bool success = free_page_.Add(
      alloc_address,
      1 + sequence_number_.fetch_add(1, std::memory_order_acq_rel), this);
  if (!success)
    Shutdown();
  return success;
}

void Client::Shutdown() {
  inited_.store(false, std::memory_order_release);
}

}  // namespace profiling
}  // namespace perfetto
<commit_msg>heapprofd client control sock: correct err checking on free batch flush<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/profiling/memory/client.h"

#include <inttypes.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

#include <atomic>
#include <new>

#include <unwindstack/MachineArm.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/MachineMips.h>
#include <unwindstack/MachineMips64.h>
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>

#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/thread_utils.h"
#include "perfetto/base/unix_socket.h"
#include "perfetto/base/utils.h"
#include "src/profiling/memory/sampler.h"
#include "src/profiling/memory/scoped_spinlock.h"
#include "src/profiling/memory/wire_protocol.h"

namespace perfetto {
namespace profiling {
namespace {

const char kSingleByte[1] = {'x'};
constexpr std::chrono::seconds kLockTimeout{1};

base::Optional<base::UnixSocketRaw> Connect(const std::string& sock_name) {
  auto sock = base::UnixSocketRaw::CreateMayFail(base::SockType::kStream);
  if (!sock || !sock.Connect(sock_name)) {
    PERFETTO_PLOG("Failed to connect to %s", sock_name.c_str());
    return base::nullopt;
  }
  if (!sock.SetTxTimeout(kClientSockTimeoutMs)) {
    PERFETTO_PLOG("Failed to set send timeout for %s", sock_name.c_str());
    return base::nullopt;
  }
  if (!sock.SetRxTimeout(kClientSockTimeoutMs)) {
    PERFETTO_PLOG("Failed to set receive timeout for %s", sock_name.c_str());
    return base::nullopt;
  }
  return std::move(sock);
}

inline bool IsMainThread() {
  return getpid() == base::GetThreadId();
}

// TODO(b/117203899): Remove this after making bionic implementation safe to
// use.
char* FindMainThreadStack() {
  base::ScopedFstream maps(fopen("/proc/self/maps", "r"));
  if (!maps) {
    return nullptr;
  }
  while (!feof(*maps)) {
    char line[1024];
    char* data = fgets(line, sizeof(line), *maps);
    if (data != nullptr && strstr(data, "[stack]")) {
      char* sep = strstr(data, "-");
      if (sep == nullptr)
        continue;
      sep++;
      return reinterpret_cast<char*>(strtoll(sep, nullptr, 16));
    }
  }
  return nullptr;
}

int UnsetDumpable(int) {
  prctl(PR_SET_DUMPABLE, 0);
  return 0;
}

}  // namespace

bool FreePage::Add(const uint64_t addr,
                   const uint64_t sequence_number,
                   Client* client) {
  std::unique_lock<std::timed_mutex> l(mutex_, kLockTimeout);
  if (!l.owns_lock())
    return false;
  if (free_page_.num_entries == kFreePageSize) {
    if (!client->FlushFrees(&free_page_))
      return false;
    // Now that we have flushed, reset to after the header.
    free_page_.num_entries = 0;
  }
  FreePageEntry& current_entry = free_page_.entries[free_page_.num_entries++];
  current_entry.sequence_number = sequence_number;
  current_entry.addr = addr;
  return true;
}

bool Client::FlushFrees(FreeMetadata* free_metadata) {
  WireMessage msg = {};
  msg.record_type = RecordType::Free;
  msg.free_header = free_metadata;
  if (!SendWireMessage(&shmem_, msg)) {
    PERFETTO_PLOG("Failed to send wire message");
    Shutdown();
    return false;
  }
  if (sock_.Send(kSingleByte, sizeof(kSingleByte)) == -1) {
    Shutdown();
    return false;
  }
  return true;
}

const char* GetThreadStackBase() {
  pthread_attr_t attr;
  if (pthread_getattr_np(pthread_self(), &attr) != 0)
    return nullptr;
  base::ScopedResource<pthread_attr_t*, pthread_attr_destroy, nullptr> cleanup(
      &attr);

  char* stackaddr;
  size_t stacksize;
  if (pthread_attr_getstack(&attr, reinterpret_cast<void**>(&stackaddr),
                            &stacksize) != 0)
    return nullptr;
  return stackaddr + stacksize;
}

Client::Client(base::Optional<base::UnixSocketRaw> sock)
    : sampler_(8192),  // placeholder until we receive the config (within ctor)
      main_thread_stack_base_(FindMainThreadStack()) {
  if (!sock || !sock.value()) {
    PERFETTO_DFATAL("Socket not connected.");
    return;
  }
  sock_ = std::move(sock.value());

  // We might be running in a process that is not dumpable (such as app
  // processes on user builds), in which case the /proc/self/mem will be chown'd
  // to root:root, and will not be accessible even to the process itself (see
  // man 5 proc). In such situations, temporarily mark the process dumpable to
  // be able to open the files, unsetting dumpability immediately afterwards.
  int orig_dumpable = prctl(PR_GET_DUMPABLE);

  enum { kNop, kDoUnset };
  base::ScopedResource<int, UnsetDumpable, kNop, false> unset_dumpable(kNop);
  if (orig_dumpable == 0) {
    unset_dumpable.reset(kDoUnset);
    prctl(PR_SET_DUMPABLE, 1);
  }

  base::ScopedFile maps(base::OpenFile("/proc/self/maps", O_RDONLY));
  if (!maps) {
    PERFETTO_DFATAL("Failed to open /proc/self/maps");
    return;
  }
  base::ScopedFile mem(base::OpenFile("/proc/self/mem", O_RDONLY));
  if (!mem) {
    PERFETTO_DFATAL("Failed to open /proc/self/mem");
    return;
  }
  // Restore original dumpability value if we overrode it.
  unset_dumpable.reset();

  int fds[kHandshakeSize];
  fds[kHandshakeMaps] = *maps;
  fds[kHandshakeMem] = *mem;

  // Send an empty record to transfer fds for /proc/self/maps and
  // /proc/self/mem.
  if (sock_.Send(kSingleByte, sizeof(kSingleByte), fds, kHandshakeSize) !=
      sizeof(kSingleByte)) {
    PERFETTO_DFATAL("Failed to send file descriptors.");
    return;
  }

  base::ScopedFile shmem_fd;
  if (sock_.Receive(&client_config_, sizeof(client_config_), &shmem_fd, 1) !=
      sizeof(client_config_)) {
    PERFETTO_DFATAL("Failed to receive client config.");
    return;
  }

  auto shmem = SharedRingBuffer::Attach(std::move(shmem_fd));
  if (!shmem || !shmem->is_valid()) {
    PERFETTO_DFATAL("Failed to attach to shmem.");
    return;
  }
  shmem_ = std::move(shmem.value());

  PERFETTO_DCHECK(client_config_.interval >= 1);
  sampler_ = Sampler(client_config_.interval);

  PERFETTO_DLOG("Initialized client.");
  inited_.store(true, std::memory_order_release);
}

Client::Client(const std::string& sock_name) : Client(Connect(sock_name)) {}

const char* Client::GetStackBase() {
  if (IsMainThread()) {
    if (!main_thread_stack_base_)
      // Because pthread_attr_getstack reads and parses /proc/self/maps and
      // /proc/self/stat, we have to cache the result here.
      main_thread_stack_base_ = GetThreadStackBase();
    return main_thread_stack_base_;
  }
  return GetThreadStackBase();
}

// The stack grows towards numerically smaller addresses, so the stack layout
// of main calling malloc is as follows.
//
//               +------------+
//               |SendWireMsg |
// stacktop +--> +------------+ 0x1000
//               |RecordMalloc|    +
//               +------------+    |
//               | malloc     |    |
//               +------------+    |
//               |  main      |    v
// stackbase +-> +------------+ 0xffff
bool Client::RecordMalloc(uint64_t alloc_size,
                          uint64_t total_size,
                          uint64_t alloc_address) {
  if (!inited_.load(std::memory_order_acquire)) {
    return false;
  }
  AllocMetadata metadata;
  const char* stackbase = GetStackBase();
  const char* stacktop = reinterpret_cast<char*>(__builtin_frame_address(0));
  unwindstack::AsmGetRegs(metadata.register_data);

  if (stackbase < stacktop) {
    PERFETTO_DFATAL("Stackbase >= stacktop.");
    Shutdown();
    return false;
  }

  uint64_t stack_size = static_cast<uint64_t>(stackbase - stacktop);
  metadata.total_size = total_size;
  metadata.alloc_size = alloc_size;
  metadata.alloc_address = alloc_address;
  metadata.stack_pointer = reinterpret_cast<uint64_t>(stacktop);
  metadata.stack_pointer_offset = sizeof(AllocMetadata);
  metadata.arch = unwindstack::Regs::CurrentArch();
  metadata.sequence_number =
      1 + sequence_number_.fetch_add(1, std::memory_order_acq_rel);

  WireMessage msg{};
  msg.record_type = RecordType::Malloc;
  msg.alloc_header = &metadata;
  msg.payload = const_cast<char*>(stacktop);
  msg.payload_size = static_cast<size_t>(stack_size);

  if (!SendWireMessage(&shmem_, msg)) {
    PERFETTO_PLOG("Failed to send wire message.");
    Shutdown();
    return false;
  }
  if (sock_.Send(kSingleByte, sizeof(kSingleByte)) == -1) {
    PERFETTO_PLOG("Failed to send wire message.");
    Shutdown();
    return false;
  }
  return true;
}

bool Client::RecordFree(uint64_t alloc_address) {
  if (!inited_.load(std::memory_order_acquire))
    return false;
  bool success = free_page_.Add(
      alloc_address,
      1 + sequence_number_.fetch_add(1, std::memory_order_acq_rel), this);
  if (!success)
    Shutdown();
  return success;
}

void Client::Shutdown() {
  inited_.store(false, std::memory_order_release);
}

}  // namespace profiling
}  // namespace perfetto
<|endoftext|>