RetroArch/menu/menu_animation.c

688 lines
18 KiB
C
Raw Normal View History

/* RetroArch - A frontend for libretro.
* Copyright (C) 2014-2016 - Jean-André Santoni
2016-01-10 03:06:50 +00:00
* Copyright (C) 2011-2016 - Daniel De Matteis
*
* RetroArch 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch 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 RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <string.h>
#include <compat/strl.h>
#include <encodings/utf.h>
#include <retro_miscellaneous.h>
#include <features/features_cpu.h>
2015-06-05 16:22:15 +00:00
2015-09-26 23:10:15 +00:00
#include "menu_animation.h"
#include "../configuration.h"
2016-05-10 06:53:14 +00:00
#include "../performance_counters.h"
2016-02-25 15:06:23 +00:00
#define IDEAL_DELTA_TIME (1.0 / 60.0 * 1000000.0)
2015-09-06 00:41:36 +00:00
struct tween
{
2015-09-26 22:42:27 +00:00
bool alive;
float duration;
float running_since;
float initial_value;
float target_value;
float *subject;
int tag;
easing_cb easing;
tween_cb cb;
2015-09-06 00:41:36 +00:00
};
2015-09-06 02:18:26 +00:00
struct menu_animation
2015-09-06 00:41:36 +00:00
{
struct tween *list;
size_t capacity;
size_t size;
size_t first_dead;
2015-09-06 02:18:26 +00:00
};
2015-09-06 00:41:36 +00:00
typedef struct menu_animation menu_animation_t;
2017-01-09 15:39:09 +00:00
static menu_animation_t anim;
static retro_time_t cur_time = 0;
static retro_time_t old_time = 0;
static float delta_time = 0.0f;
static bool animation_is_active = false;
2015-02-11 00:15:16 +00:00
/* from https://github.com/kikito/tween.lua/blob/master/tween.lua */
2015-02-11 00:15:16 +00:00
static float easing_linear(float t, float b, float c, float d)
2015-02-10 23:40:18 +00:00
{
2015-02-11 00:15:16 +00:00
return c * t / d + b;
}
2015-02-11 00:15:16 +00:00
static float easing_in_out_quad(float t, float b, float c, float d)
{
2015-02-11 00:15:16 +00:00
t = t / d * 2;
if (t < 1)
return c / 2 * pow(t, 2) + b;
return -c / 2 * ((t - 1) * (t - 3) - 1) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_in_quad(float t, float b, float c, float d)
{
return c * pow(t / d, 2) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_quad(float t, float b, float c, float d)
{
t = t / d;
return -c * t * (t - 2) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_in_quad(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_out_quad(t * 2, b, c / 2, d);
return easing_in_quad((t * 2) - d, b + c / 2, c / 2, d);
}
2015-02-11 00:15:16 +00:00
static float easing_in_cubic(float t, float b, float c, float d)
{
return c * pow(t / d, 3) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_cubic(float t, float b, float c, float d)
{
return c * (pow(t / d - 1, 3) + 1) + b;
}
static float easing_in_out_cubic(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return c / 2 * t * t * t + b;
t = t - 2;
return c / 2 * (t * t * t + 2) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_in_cubic(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_out_cubic(t * 2, b, c / 2, d);
return easing_in_cubic((t * 2) - d, b + c / 2, c / 2, d);
}
2015-02-11 00:15:16 +00:00
static float easing_in_quart(float t, float b, float c, float d)
{
return c * pow(t / d, 4) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_quart(float t, float b, float c, float d)
{
return -c * (pow(t / d - 1, 4) - 1) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_in_out_quart(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return c / 2 * pow(t, 4) + b;
return -c / 2 * (pow(t - 2, 4) - 2) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_in_quart(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_out_quart(t * 2, b, c / 2, d);
return easing_in_quart((t * 2) - d, b + c / 2, c / 2, d);
}
2015-02-11 00:15:16 +00:00
static float easing_in_quint(float t, float b, float c, float d)
{
return c * pow(t / d, 5) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_quint(float t, float b, float c, float d)
{
return c * (pow(t / d - 1, 5) + 1) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_in_out_quint(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return c / 2 * pow(t, 5) + b;
return c / 2 * (pow(t - 2, 5) + 2) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_in_quint(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_out_quint(t * 2, b, c / 2, d);
return easing_in_quint((t * 2) - d, b + c / 2, c / 2, d);
}
2015-02-11 00:15:16 +00:00
static float easing_in_sine(float t, float b, float c, float d)
{
return -c * cos(t / d * (M_PI / 2)) + c + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_sine(float t, float b, float c, float d)
{
return c * sin(t / d * (M_PI / 2)) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_in_out_sine(float t, float b, float c, float d)
{
return -c / 2 * (cos(M_PI * t / d) - 1) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_in_sine(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_out_sine(t * 2, b, c / 2, d);
return easing_in_sine((t * 2) -d, b + c / 2, c / 2, d);
}
2015-02-11 00:15:16 +00:00
static float easing_in_expo(float t, float b, float c, float d)
{
if (t == 0)
return b;
2014-10-22 00:05:01 +00:00
return c * powf(2, 10 * (t / d - 1)) + b - c * 0.001;
}
2015-02-11 00:15:16 +00:00
static float easing_out_expo(float t, float b, float c, float d)
{
if (t == d)
return b + c;
2014-10-22 00:05:01 +00:00
return c * 1.001 * (-powf(2, -10 * t / d) + 1) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_in_out_expo(float t, float b, float c, float d)
{
if (t == 0)
return b;
if (t == d)
return b + c;
t = t / d * 2;
if (t < 1)
2014-10-22 00:05:01 +00:00
return c / 2 * powf(2, 10 * (t - 1)) + b - c * 0.0005;
return c / 2 * 1.0005 * (-powf(2, -10 * (t - 1)) + 2) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_in_expo(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_out_expo(t * 2, b, c / 2, d);
return easing_in_expo((t * 2) - d, b + c / 2, c / 2, d);
}
2015-02-11 00:15:16 +00:00
static float easing_in_circ(float t, float b, float c, float d)
{
2014-10-22 00:05:01 +00:00
return(-c * (sqrt(1 - powf(t / d, 2)) - 1) + b);
}
2015-02-11 00:15:16 +00:00
static float easing_out_circ(float t, float b, float c, float d)
{
2014-10-22 00:05:01 +00:00
return(c * sqrt(1 - powf(t / d - 1, 2)) + b);
}
2015-02-11 00:15:16 +00:00
static float easing_in_out_circ(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return -c / 2 * (sqrt(1 - t * t) - 1) + b;
t = t - 2;
return c / 2 * (sqrt(1 - t * t) + 1) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_in_circ(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_out_circ(t * 2, b, c / 2, d);
return easing_in_circ((t * 2) - d, b + c / 2, c / 2, d);
}
2015-02-11 00:15:16 +00:00
static float easing_out_bounce(float t, float b, float c, float d)
{
t = t / d;
if (t < 1 / 2.75)
return c * (7.5625 * t * t) + b;
if (t < 2 / 2.75)
{
t = t - (1.5 / 2.75);
return c * (7.5625 * t * t + 0.75) + b;
}
else if (t < 2.5 / 2.75)
{
t = t - (2.25 / 2.75);
return c * (7.5625 * t * t + 0.9375) + b;
}
t = t - (2.625 / 2.75);
return c * (7.5625 * t * t + 0.984375) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_in_bounce(float t, float b, float c, float d)
{
2015-02-11 00:15:16 +00:00
return c - easing_out_bounce(d - t, 0, c, d) + b;
}
2015-02-11 00:15:16 +00:00
static float easing_in_out_bounce(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_in_bounce(t * 2, 0, c, d) * 0.5 + b;
return easing_out_bounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b;
}
2015-02-11 00:15:16 +00:00
static float easing_out_in_bounce(float t, float b, float c, float d)
{
if (t < d / 2)
2015-02-11 00:15:16 +00:00
return easing_out_bounce(t * 2, b, c / 2, d);
return easing_in_bounce((t * 2) - d, b + c / 2, c / 2, d);
}
2015-09-25 20:28:08 +00:00
static void menu_animation_ticker_generic(uint64_t idx,
size_t max_width, size_t *offset, size_t *width)
{
2017-01-17 13:15:43 +00:00
int ticker_period = 2 * (*width - max_width) + 4;
int phase = idx % ticker_period;
2015-09-25 20:28:08 +00:00
2017-01-17 13:15:43 +00:00
int phase_left_stop = 2;
int phase_left_moving = phase_left_stop + (*width - max_width);
int phase_right_stop = phase_left_moving + 2;
2015-09-25 20:28:08 +00:00
2017-01-17 13:15:43 +00:00
int left_offset = phase - phase_left_stop;
int right_offset = (*width - max_width) - (phase - phase_right_stop);
2015-09-25 20:28:08 +00:00
if (phase < phase_left_stop)
*offset = 0;
else if (phase < phase_left_moving)
*offset = left_offset;
2015-09-25 20:28:08 +00:00
else if (phase < phase_right_stop)
*offset = *width - max_width;
2015-09-25 20:28:08 +00:00
else
*offset = right_offset;
2015-09-25 20:28:08 +00:00
*width = max_width;
}
2016-02-04 20:20:28 +00:00
static void menu_animation_push_internal(menu_animation_t *anim,
const struct tween *t)
2015-09-25 20:28:08 +00:00
{
struct tween *target = NULL;
if (anim->first_dead < anim->size && !anim->list[anim->first_dead].alive)
target = &anim->list[anim->first_dead++];
else
{
if (anim->size >= anim->capacity)
{
anim->capacity++;
anim->list = (struct tween*)realloc(anim->list,
anim->capacity * sizeof(struct tween));
}
target = &anim->list[anim->size++];
}
*target = *t;
}
static bool menu_animation_push(menu_animation_t *anim, menu_animation_ctx_entry_t *entry)
2015-02-11 00:15:16 +00:00
{
struct tween t;
2015-02-11 00:15:16 +00:00
2016-02-25 14:44:30 +00:00
if (!entry || !entry->subject)
return false;
t.alive = true;
2016-02-25 14:44:30 +00:00
t.duration = entry->duration;
t.running_since = 0;
2016-02-25 14:44:30 +00:00
t.initial_value = *entry->subject;
t.target_value = entry->target_value;
t.subject = entry->subject;
t.tag = entry->tag;
t.cb = entry->cb;
t.easing = NULL;
switch (entry->easing_enum)
2015-02-11 00:15:16 +00:00
{
case EASING_LINEAR:
t.easing = &easing_linear;
2015-02-11 00:15:16 +00:00
break;
/* Quad */
case EASING_IN_QUAD:
t.easing = &easing_in_quad;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_QUAD:
t.easing = &easing_out_quad;
2015-02-11 00:15:16 +00:00
break;
case EASING_IN_OUT_QUAD:
t.easing = &easing_in_out_quad;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_IN_QUAD:
t.easing = &easing_out_in_quad;
2015-02-11 00:15:16 +00:00
break;
/* Cubic */
case EASING_IN_CUBIC:
t.easing = &easing_in_cubic;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_CUBIC:
t.easing = &easing_out_cubic;
2015-02-11 00:15:16 +00:00
break;
case EASING_IN_OUT_CUBIC:
t.easing = &easing_in_out_cubic;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_IN_CUBIC:
t.easing = &easing_out_in_cubic;
2015-02-11 00:15:16 +00:00
break;
/* Quart */
case EASING_IN_QUART:
t.easing = &easing_in_quart;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_QUART:
t.easing = &easing_out_quart;
2015-02-11 00:15:16 +00:00
break;
case EASING_IN_OUT_QUART:
t.easing = &easing_in_out_quart;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_IN_QUART:
t.easing = &easing_out_in_quart;
2015-02-11 00:15:16 +00:00
break;
/* Quint */
case EASING_IN_QUINT:
t.easing = &easing_in_quint;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_QUINT:
t.easing = &easing_out_quint;
2015-02-11 00:15:16 +00:00
break;
case EASING_IN_OUT_QUINT:
t.easing = &easing_in_out_quint;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_IN_QUINT:
t.easing = &easing_out_in_quint;
2015-02-11 00:15:16 +00:00
break;
/* Sine */
case EASING_IN_SINE:
t.easing = &easing_in_sine;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_SINE:
t.easing = &easing_out_sine;
2015-02-11 00:15:16 +00:00
break;
case EASING_IN_OUT_SINE:
t.easing = &easing_in_out_sine;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_IN_SINE:
t.easing = &easing_out_in_sine;
2015-02-11 00:15:16 +00:00
break;
/* Expo */
case EASING_IN_EXPO:
t.easing = &easing_in_expo;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_EXPO:
t.easing = &easing_out_expo;
2015-02-11 00:15:16 +00:00
break;
case EASING_IN_OUT_EXPO:
t.easing = &easing_in_out_expo;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_IN_EXPO:
t.easing = &easing_out_in_expo;
2015-02-11 00:15:16 +00:00
break;
/* Circ */
case EASING_IN_CIRC:
t.easing = &easing_in_circ;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_CIRC:
t.easing = &easing_out_circ;
2015-02-11 00:15:16 +00:00
break;
case EASING_IN_OUT_CIRC:
t.easing = &easing_in_out_circ;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_IN_CIRC:
t.easing = &easing_out_in_circ;
2015-02-11 00:15:16 +00:00
break;
/* Bounce */
case EASING_IN_BOUNCE:
t.easing = &easing_in_bounce;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_BOUNCE:
t.easing = &easing_out_bounce;
2015-02-11 00:15:16 +00:00
break;
case EASING_IN_OUT_BOUNCE:
t.easing = &easing_in_out_bounce;
2015-02-11 00:15:16 +00:00
break;
case EASING_OUT_IN_BOUNCE:
t.easing = &easing_out_in_bounce;
2015-02-11 00:15:16 +00:00
break;
default:
break;
}
/* ignore born dead tweens */
if (!t.easing || t.duration == 0 || t.initial_value == t.target_value)
return false;
2015-09-25 20:28:08 +00:00
menu_animation_push_internal(anim, &t);
2015-02-11 00:15:16 +00:00
return true;
}
2017-01-09 15:39:09 +00:00
bool menu_animation_update(float delta_time)
{
unsigned i;
unsigned active_tweens = 0;
for(i = 0; i < anim.size; i++)
{
struct tween *tween = &anim.list[i];
if (!tween || !tween->alive)
continue;
tween->running_since += delta_time;
*tween->subject = tween->easing(
tween->running_since,
tween->initial_value,
tween->target_value - tween->initial_value,
tween->duration);
if (tween->running_since >= tween->duration)
{
*tween->subject = tween->target_value;
tween->alive = false;
if (i < anim.first_dead)
anim.first_dead = i;
if (tween->cb)
tween->cb();
}
if (tween->running_since < tween->duration)
active_tweens += 1;
}
if (!active_tweens)
{
anim.size = 0;
anim.first_dead = 0;
return false;
}
animation_is_active = true;
return true;
}
2017-01-17 15:29:23 +00:00
bool menu_animation_ticker(const menu_animation_ctx_ticker_t *ticker)
{
size_t str_len = utf8len(ticker->str);
size_t offset = 0;
if ((size_t)str_len <= ticker->len)
{
utf8cpy(ticker->s,
PATH_MAX_LENGTH,
ticker->str,
ticker->len);
return true;
}
if (!ticker->selected)
{
utf8cpy(ticker->s, PATH_MAX_LENGTH, ticker->str, ticker->len - 3);
strlcat(ticker->s, "...", PATH_MAX_LENGTH);
return true;
}
if (str_len > ticker->len)
menu_animation_ticker_generic(
ticker->idx,
ticker->len,
&offset,
&str_len);
utf8cpy(
ticker->s,
PATH_MAX_LENGTH,
utf8skip(ticker->str, offset),
str_len);
animation_is_active = true;
return true;
}
bool menu_animation_get_ideal_delta_time(menu_animation_ctx_delta_t *delta)
{
if (!delta)
return false;
delta->ideal = delta->current / IDEAL_DELTA_TIME;
return true;
}
2015-09-25 21:33:11 +00:00
bool menu_animation_ctl(enum menu_animation_ctl_state state, void *data)
2015-09-06 00:41:36 +00:00
{
2015-09-25 21:33:11 +00:00
switch (state)
{
2015-12-11 11:00:49 +00:00
case MENU_ANIMATION_CTL_DEINIT:
2016-02-25 12:26:51 +00:00
{
size_t i;
2016-02-25 14:47:03 +00:00
for (i = 0; i < anim.size; i++)
2016-02-25 12:26:51 +00:00
{
2016-02-25 14:47:03 +00:00
if (anim.list[i].subject)
anim.list[i].subject = NULL;
2016-02-25 12:26:51 +00:00
}
2016-02-25 14:47:03 +00:00
free(anim.list);
2016-02-25 12:26:51 +00:00
2016-02-25 14:47:03 +00:00
memset(&anim, 0, sizeof(menu_animation_t));
2016-02-25 12:26:51 +00:00
}
cur_time = 0;
old_time = 0;
delta_time = 0.0f;
2015-12-11 11:00:49 +00:00
break;
2015-09-25 21:33:11 +00:00
case MENU_ANIMATION_CTL_IS_ACTIVE:
return animation_is_active;
case MENU_ANIMATION_CTL_CLEAR_ACTIVE:
animation_is_active = false;
2016-02-11 00:54:09 +00:00
break;
2015-09-25 21:42:00 +00:00
case MENU_ANIMATION_CTL_SET_ACTIVE:
animation_is_active = true;
2016-02-11 00:54:09 +00:00
break;
2015-09-25 21:37:02 +00:00
case MENU_ANIMATION_CTL_DELTA_TIME:
{
float *ptr = (float*)data;
if (!ptr)
return false;
*ptr = delta_time;
2015-09-25 21:37:02 +00:00
}
2016-02-11 00:54:09 +00:00
break;
case MENU_ANIMATION_CTL_UPDATE_TIME:
{
static retro_time_t last_clock_update = 0;
settings_t *settings = config_get_ptr();
2016-05-10 07:17:04 +00:00
cur_time = cpu_features_get_time_usec();
delta_time = cur_time - old_time;
2016-02-25 15:06:23 +00:00
if (delta_time >= IDEAL_DELTA_TIME* 4)
delta_time = IDEAL_DELTA_TIME * 4;
if (delta_time <= IDEAL_DELTA_TIME / 4)
delta_time = IDEAL_DELTA_TIME / 4;
old_time = cur_time;
if (((cur_time - last_clock_update) > 1000000)
&& settings->menu.timedate_enable)
{
animation_is_active = true;
last_clock_update = cur_time;
}
}
2016-02-11 00:54:09 +00:00
break;
2016-02-25 12:23:39 +00:00
case MENU_ANIMATION_CTL_KILL_BY_TAG:
{
unsigned i;
menu_animation_ctx_tag_t *tag = (menu_animation_ctx_tag_t*)data;
if (!tag || tag->id == -1)
return false;
2016-02-25 14:47:03 +00:00
for (i = 0; i < anim.size; ++i)
2016-02-25 12:23:39 +00:00
{
2016-02-25 14:47:03 +00:00
if (anim.list[i].tag != tag->id)
2016-02-25 12:23:39 +00:00
continue;
2016-02-25 14:47:03 +00:00
anim.list[i].alive = false;
anim.list[i].subject = NULL;
2016-02-25 12:23:39 +00:00
2016-02-25 14:47:03 +00:00
if (i < anim.first_dead)
anim.first_dead = i;
2016-02-25 12:23:39 +00:00
}
}
break;
2016-02-25 14:05:18 +00:00
case MENU_ANIMATION_CTL_KILL_BY_SUBJECT:
{
unsigned i, j, killed = 0;
menu_animation_ctx_subject_t *subject =
(menu_animation_ctx_subject_t*)data;
float **sub = (float**)subject->data;
2016-02-25 14:47:03 +00:00
for (i = 0; i < anim.size; ++i)
2016-02-25 14:05:18 +00:00
{
2016-02-25 14:47:03 +00:00
if (!anim.list[i].alive)
2016-02-25 14:05:18 +00:00
continue;
for (j = 0; j < subject->count; ++j)
{
2016-02-25 14:47:03 +00:00
if (anim.list[i].subject != sub[j])
2016-02-25 14:05:18 +00:00
continue;
2016-02-25 14:47:03 +00:00
anim.list[i].alive = false;
anim.list[i].subject = NULL;
2016-02-25 14:05:18 +00:00
2016-02-25 14:47:03 +00:00
if (i < anim.first_dead)
anim.first_dead = i;
2016-02-25 14:05:18 +00:00
killed++;
break;
}
}
}
break;
2016-02-25 14:44:30 +00:00
case MENU_ANIMATION_CTL_PUSH:
return menu_animation_push(&anim, (menu_animation_ctx_entry_t *)data);
2016-02-11 00:54:09 +00:00
case MENU_ANIMATION_CTL_NONE:
2016-02-25 14:05:18 +00:00
default:
2016-02-11 00:54:09 +00:00
break;
2015-09-25 21:33:11 +00:00
}
2016-02-11 00:54:09 +00:00
return true;
2015-09-06 00:41:36 +00:00
}