2012-12-24 19:41:12 +01:00
/*
* Copyright ( c ) Sebastian Krzyszkowiak < dos @ dosowisko . net >
*
* 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
2017-07-22 18:22:12 +02:00
* the Free Software Foundation ; either version 3 of the License , or
2012-12-24 19:41:12 +01:00
* ( 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
2017-07-22 18:22:12 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2012-12-24 19:41:12 +01:00
*/
2017-09-10 21:35:14 +02:00
# include "internal.h"
2018-11-30 02:45:39 +01:00
# include <ctype.h>
2018-11-02 06:07:49 +01:00
# ifdef ALLEGRO_ANDROID
# include <android/log.h>
# endif
2012-12-24 19:41:12 +01:00
2018-05-30 21:11:46 +02:00
// TODO: split to separate files
2019-03-29 20:53:45 +01:00
ALLEGRO_DEBUG_CHANNEL ( " libsuperderpy " )
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT void DrawVerticalGradientRect ( float x , float y , float w , float h , ALLEGRO_COLOR top , ALLEGRO_COLOR bottom ) {
2012-12-24 19:41:12 +01:00
ALLEGRO_VERTEX v [ ] = {
2019-03-29 03:24:36 +01:00
{ . x = x , . y = y , . z = 0 , . color = top } ,
{ . x = x + w , . y = y , . z = 0 , . color = top } ,
{ . x = x , . y = y + h , . z = 0 , . color = bottom } ,
{ . x = x + w , . y = y + h , . z = 0 , . color = bottom } } ;
2012-12-24 19:41:12 +01:00
al_draw_prim ( v , NULL , NULL , 0 , 4 , ALLEGRO_PRIM_TRIANGLE_STRIP ) ;
}
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT void DrawHorizontalGradientRect ( float x , float y , float w , float h , ALLEGRO_COLOR left , ALLEGRO_COLOR right ) {
2012-12-24 19:41:12 +01:00
ALLEGRO_VERTEX v [ ] = {
2019-03-29 03:24:36 +01:00
{ . x = x , . y = y , . z = 0 , . color = left } ,
{ . x = x + w , . y = y , . z = 0 , . color = right } ,
{ . x = x , . y = y + h , . z = 0 , . color = left } ,
{ . x = x + w , . y = y + h , . z = 0 , . color = right } } ;
2012-12-24 19:41:12 +01:00
al_draw_prim ( v , NULL , NULL , 0 , 4 , ALLEGRO_PRIM_TRIANGLE_STRIP ) ;
}
2017-09-10 21:35:14 +02:00
SYMBOL_EXPORT void DrawTextWithShadow ( ALLEGRO_FONT * font , ALLEGRO_COLOR color , float x , float y , int flags , char const * text ) {
2018-02-03 03:46:33 +01:00
// TODO: consider using a set of shaders
2017-09-10 21:35:14 +02:00
al_draw_text ( font , al_map_rgba ( 0 , 0 , 0 , 128 ) , x + 1 , y + 1 , flags , text ) ;
2016-09-08 01:42:48 +02:00
al_draw_text ( font , color , x , y , flags , text ) ;
2012-12-24 19:41:12 +01:00
}
2017-09-10 21:35:14 +02:00
SYMBOL_EXPORT int DrawWrappedText ( ALLEGRO_FONT * font , ALLEGRO_COLOR color , float x , float y , int width , int flags , char const * text ) {
2017-08-19 01:26:53 +02:00
// TODO: use al_do_multiline_text; and switch to al_draw_multiline_text once it returns number of lines
2016-09-08 00:32:21 +02:00
char stext [ 1024 ] ; // Copy of the passed text.
2017-09-10 21:35:14 +02:00
char * pch ; // A pointer to each word.
2016-09-08 00:32:21 +02:00
char word [ 255 ] ; // A string containing the word (for convienence)
2017-09-10 21:35:14 +02:00
char * breakchar = " \n " ;
2016-09-08 00:32:21 +02:00
char lines [ 40 ] [ 1024 ] ; // A lovely array of strings to hold all the lines (40 max atm)
char temp [ 1024 ] ; // Holds the string data of the current line only.
int line = 0 ; // Counts which line we are currently using.
int height = al_get_font_line_height ( font ) + 1 ;
// Setup our strings
2018-06-30 01:50:29 +02:00
strncpy ( stext , text , 1023 ) ;
2018-12-11 00:54:43 +01:00
strncpy ( temp , " " , 1023 ) ;
2017-09-10 21:35:14 +02:00
for ( int i = 0 ; i < 40 ; i + = 1 ) {
strncpy ( lines [ i ] , " " , 1024 ) ;
2016-09-08 00:32:21 +02:00
}
//-------------------- Code Begins
2018-11-30 04:20:35 +01:00
char * context = NULL ;
2016-09-08 00:32:21 +02:00
2017-09-10 21:35:14 +02:00
pch = strtok_r ( stext , " " , & context ) ; // Get the first word.
2016-09-08 00:32:21 +02:00
do {
2017-09-10 21:35:14 +02:00
snprintf ( word , 255 , " %s " , pch ) ;
strncat ( temp , word , 255 ) ; // Append the word to the end of TempLine
2016-09-08 00:32:21 +02:00
// This code checks for the new line character.
2017-09-10 21:35:14 +02:00
if ( strncmp ( word , breakchar , 255 ) = = 0 ) {
line + = 1 ; // Move down a Line
2018-12-11 00:54:43 +01:00
strncpy ( temp , " " , 1023 ) ; // Clear the tempstring
2016-09-08 00:32:21 +02:00
} else {
2017-09-10 21:35:14 +02:00
if ( al_get_text_width ( font , temp ) > = ( width ) ) { // Check if text is larger than the area.
strncpy ( temp , word , 255 ) ; // clear the templine and add the word to it.
line + = 1 ; // Move to the next line.
2016-09-08 00:32:21 +02:00
}
if ( line < 40 ) {
2017-09-10 21:35:14 +02:00
strncat ( lines [ line ] , word , 255 ) ; // Append the word to whatever line we are currently on.
2016-09-08 00:32:21 +02:00
}
}
2017-09-10 21:35:14 +02:00
pch = strtok_r ( NULL , " " , & context ) ; // Get the next word.
2016-09-08 00:32:21 +02:00
} while ( pch ! = NULL ) ;
// ---------------------------------- Time to draw.
2017-09-10 21:35:14 +02:00
for ( int i = 0 ; i < = line ; i + = 1 ) { // Move through each line and draw according to the passed flags.
2016-09-08 00:32:21 +02:00
switch ( flags ) {
case ALLEGRO_ALIGN_CENTER :
2018-07-04 19:08:39 +02:00
al_draw_text ( font , color , x + ( width / 2.0 ) , y + ( i * height ) , ALLEGRO_ALIGN_CENTER , lines [ i ] ) ;
2016-09-08 00:32:21 +02:00
break ;
case ALLEGRO_ALIGN_RIGHT :
2016-09-08 01:42:48 +02:00
al_draw_text ( font , color , x + width , y + ( i * height ) , ALLEGRO_ALIGN_RIGHT , lines [ i ] ) ;
2016-09-08 00:32:21 +02:00
break ;
case ALLEGRO_ALIGN_LEFT :
default :
2016-09-08 01:42:48 +02:00
al_draw_text ( font , color , x , y + ( i * height ) , ALLEGRO_ALIGN_LEFT , lines [ i ] ) ;
2016-09-08 00:32:21 +02:00
break ;
}
}
2017-09-10 21:35:14 +02:00
return ( ( line + 1 ) * height ) ; // Return the actual height of the text in pixels.
2016-09-08 00:32:21 +02:00
}
2017-09-10 21:35:14 +02:00
SYMBOL_EXPORT int DrawWrappedTextWithShadow ( ALLEGRO_FONT * font , ALLEGRO_COLOR color , float x , float y , int width , int flags , char const * text ) {
DrawWrappedText ( font , al_map_rgba ( 0 , 0 , 0 , 128 ) , x + 1 , y + 1 , width , flags , text ) ;
2016-09-08 01:42:48 +02:00
return DrawWrappedText ( font , color , x , y , width , flags , text ) ;
}
2016-09-08 00:32:21 +02:00
2018-12-02 03:16:12 +01:00
SYMBOL_EXPORT void DrawCentered ( ALLEGRO_BITMAP * bitmap , float x , float y , int flags ) {
2018-03-20 21:38:50 +01:00
al_draw_bitmap ( bitmap , x - al_get_bitmap_width ( bitmap ) / 2.0 , y - al_get_bitmap_height ( bitmap ) / 2.0 , flags ) ;
}
2018-12-02 03:16:12 +01:00
SYMBOL_EXPORT void DrawCenteredScaled ( ALLEGRO_BITMAP * bitmap , float x , float y , double sx , double sy , int flags ) {
2018-04-24 22:06:06 +02:00
DrawCenteredTintedScaled ( bitmap , al_map_rgb ( 255 , 255 , 255 ) , x , y , sx , sy , flags ) ;
}
2018-12-02 03:16:12 +01:00
SYMBOL_EXPORT void DrawCenteredTintedScaled ( ALLEGRO_BITMAP * bitmap , ALLEGRO_COLOR tint , float x , float y , double sx , double sy , int flags ) {
2018-04-24 22:06:06 +02:00
al_draw_tinted_scaled_rotated_bitmap ( bitmap , tint , al_get_bitmap_width ( bitmap ) / 2.0 , al_get_bitmap_height ( bitmap ) / 2.0 ,
2019-03-29 03:24:36 +01:00
x , y , sx , sy , 0 , flags ) ;
2018-04-24 05:01:59 +02:00
}
2018-11-27 02:50:16 +01:00
SYMBOL_EXPORT void ClearToColor ( struct Game * game , ALLEGRO_COLOR color ) {
ALLEGRO_BITMAP * target = al_get_target_bitmap ( ) ;
2019-03-05 03:20:06 +01:00
if ( game - > _priv . current_gamestate & & GetFramebuffer ( game ) = = target & & al_get_parent_bitmap ( target ) = = al_get_backbuffer ( game - > display ) ) {
2018-11-27 02:50:16 +01:00
al_set_target_backbuffer ( game - > display ) ;
}
int x , y , w , h ;
al_get_clipping_rectangle ( & x , & y , & w , & h ) ;
al_reset_clipping_rectangle ( ) ;
al_clear_to_color ( color ) ;
al_set_clipping_rectangle ( x , y , w , h ) ;
al_set_target_bitmap ( target ) ;
}
2016-06-27 21:20:02 +02:00
/* linear filtering code written by SiegeLord */
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT ALLEGRO_COLOR InterpolateColor ( ALLEGRO_COLOR c1 , ALLEGRO_COLOR c2 , float frac ) {
2016-06-27 21:20:02 +02:00
return al_map_rgba_f ( c1 . r + frac * ( c2 . r - c1 . r ) ,
2019-03-29 03:24:36 +01:00
c1 . g + frac * ( c2 . g - c1 . g ) ,
c1 . b + frac * ( c2 . b - c1 . b ) ,
c1 . a + frac * ( c2 . a - c1 . a ) ) ;
2012-12-24 19:41:12 +01:00
}
/*! \brief Scales bitmap using software linear filtering method to current target. */
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT void ScaleBitmap ( ALLEGRO_BITMAP * source , int width , int height ) {
2017-09-10 21:35:14 +02:00
if ( ( al_get_bitmap_width ( source ) = = width ) & & ( al_get_bitmap_height ( source ) = = height ) ) {
2012-12-24 19:41:12 +01:00
al_draw_bitmap ( source , 0 , 0 , 0 ) ;
return ;
}
int x , y ;
al_lock_bitmap ( al_get_target_bitmap ( ) , ALLEGRO_PIXEL_FORMAT_ANY , ALLEGRO_LOCK_WRITEONLY ) ;
al_lock_bitmap ( source , ALLEGRO_PIXEL_FORMAT_ANY , ALLEGRO_LOCK_READONLY ) ;
for ( y = 0 ; y < height ; y + + ) {
float pixy = ( ( float ) y / height ) * ( ( float ) al_get_bitmap_height ( source ) - 1 ) ;
2018-10-10 21:37:54 +02:00
int pixy_f = ( int ) floorf ( pixy ) ;
2012-12-24 19:41:12 +01:00
for ( x = 0 ; x < width ; x + + ) {
float pixx = ( ( float ) x / width ) * ( ( float ) al_get_bitmap_width ( source ) - 1 ) ;
2018-10-10 21:37:54 +02:00
int pixx_f = ( int ) floorf ( pixx ) ;
2012-12-24 19:41:12 +01:00
ALLEGRO_COLOR a = al_get_pixel ( source , pixx_f , pixy_f ) ;
ALLEGRO_COLOR b = al_get_pixel ( source , pixx_f + 1 , pixy_f ) ;
ALLEGRO_COLOR c = al_get_pixel ( source , pixx_f , pixy_f + 1 ) ;
ALLEGRO_COLOR d = al_get_pixel ( source , pixx_f + 1 , pixy_f + 1 ) ;
2016-07-04 00:06:50 +02:00
ALLEGRO_COLOR ab = InterpolateColor ( a , b , pixx - pixx_f ) ;
ALLEGRO_COLOR cd = InterpolateColor ( c , d , pixx - pixx_f ) ;
ALLEGRO_COLOR result = InterpolateColor ( ab , cd , pixy - pixy_f ) ;
2012-12-24 19:41:12 +01:00
al_put_pixel ( x , y , result ) ;
}
}
al_unlock_bitmap ( al_get_target_bitmap ( ) ) ;
al_unlock_bitmap ( source ) ;
}
2017-09-10 21:35:14 +02:00
SYMBOL_EXPORT ALLEGRO_BITMAP * LoadScaledBitmap ( struct Game * game , char * filename , int width , int height ) {
bool memoryscale = ! strtol ( GetConfigOptionDefault ( game , " SuperDerpy " , " GPU_scaling " , " 1 " ) , NULL , 10 ) ;
2012-12-24 19:41:12 +01:00
ALLEGRO_BITMAP * source , * target = al_create_bitmap ( width , height ) ;
al_set_target_bitmap ( target ) ;
2017-09-10 21:35:14 +02:00
al_clear_to_color ( al_map_rgba ( 0 , 0 , 0 , 0 ) ) ;
2012-12-24 19:41:12 +01:00
2016-11-11 19:38:26 +01:00
int flags = al_get_new_bitmap_flags ( ) ;
if ( memoryscale ) {
al_add_new_bitmap_flag ( ALLEGRO_MEMORY_BITMAP ) ;
}
2016-06-27 21:20:02 +02:00
2018-04-08 01:34:43 +02:00
source = al_load_bitmap ( GetDataFilePath ( game , filename ) ) ;
2016-06-27 21:20:02 +02:00
if ( memoryscale ) {
2016-11-11 19:38:26 +01:00
al_set_new_bitmap_flags ( flags ) ;
2016-06-27 21:20:02 +02:00
ScaleBitmap ( source , width , height ) ;
2017-09-10 21:35:14 +02:00
} else {
2016-06-27 21:20:02 +02:00
al_draw_scaled_bitmap ( source , 0 , 0 , al_get_bitmap_width ( source ) , al_get_bitmap_height ( source ) , 0 , 0 , width , height , 0 ) ;
}
al_destroy_bitmap ( source ) ;
2012-12-24 19:41:12 +01:00
return target ;
}
2018-11-30 04:20:35 +01:00
SYMBOL_EXPORT void FatalErrorWithContext ( struct Game * game , int line , const char * file , const char * func , bool exit , char * format , . . . ) {
2017-09-10 22:07:02 +02:00
char text [ 1024 ] = { 0 } ;
2017-09-09 00:11:43 +02:00
PrintConsole ( game , " Fatal Error, displaying Blue Screen of Derp... " ) ;
va_list vl ;
va_start ( vl , format ) ;
vsnprintf ( text , 1024 , format , vl ) ;
va_end ( vl ) ;
2018-08-08 22:19:50 +02:00
fprintf ( stderr , " %s:%d [%s] \n %s \n " , file , line , func , text ) ;
2012-12-28 02:55:52 +01:00
2018-12-02 00:26:33 +01:00
# ifndef LIBSUPERDERPY_SINGLE_THREAD
2018-12-15 00:59:34 +01:00
if ( game - > _priv . loading . in_progress ) {
2018-12-02 00:26:33 +01:00
al_lock_mutex ( game - > _priv . bsod_mutex ) ;
game - > _priv . in_bsod = true ;
game - > _priv . bsod_sync = true ;
while ( game - > _priv . bsod_sync ) {
al_wait_cond ( game - > _priv . bsod_cond , game - > _priv . bsod_mutex ) ;
}
al_unlock_mutex ( game - > _priv . bsod_mutex ) ;
}
# endif
al_set_target_backbuffer ( game - > display ) ;
2017-10-30 21:17:47 +01:00
2016-06-27 21:20:02 +02:00
ALLEGRO_TRANSFORM trans ;
al_identity_transform ( & trans ) ;
al_use_transform ( & trans ) ;
2015-03-15 05:38:15 +01:00
if ( ! game - > _priv . font_bsod ) {
game - > _priv . font_bsod = al_create_builtin_font ( ) ;
}
2017-09-10 21:35:14 +02:00
al_clear_to_color ( al_map_rgb ( 0 , 0 , 170 ) ) ;
2012-12-28 03:08:36 +01:00
al_flip_display ( ) ;
2016-06-27 21:20:02 +02:00
al_rest ( 0.6 ) ;
2012-12-28 03:08:36 +01:00
2017-12-17 14:19:24 +01:00
const int offsetx = al_get_display_width ( game - > display ) / 2 ;
2018-10-10 21:37:54 +02:00
const int offsety = ( int ) ( al_get_display_height ( game - > display ) * 0.30 ) ;
2017-12-17 14:19:24 +01:00
const int fonth = al_get_font_line_height ( game - > _priv . font_bsod ) ;
2012-12-28 02:55:52 +01:00
bool done = false ;
while ( ! done ) {
al_set_target_backbuffer ( game - > display ) ;
2017-09-10 21:35:14 +02:00
al_clear_to_color ( al_map_rgb ( 0 , 0 , 170 ) ) ;
2012-12-28 02:55:52 +01:00
2018-12-15 00:59:34 +01:00
const char * header = game - > _priv . name ;
2017-12-17 14:19:24 +01:00
const int headw = al_get_text_width ( game - > _priv . font_bsod , header ) ;
2012-12-28 02:55:52 +01:00
2018-07-04 19:08:39 +02:00
al_draw_filled_rectangle ( offsetx - headw / 2.0 - 4 , offsety , 4 + offsetx + headw / 2.0 , offsety + fonth , al_map_rgb ( 170 , 170 , 170 ) ) ;
2012-12-28 02:55:52 +01:00
2018-01-20 02:44:33 +01:00
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 0 , 0 , 170 ) , offsetx , offsety , ALLEGRO_ALIGN_CENTRE , header ) ;
2012-12-28 02:55:52 +01:00
2017-09-10 21:35:14 +02:00
const char * header2 = " A fatal exception 0xD3RP has occured at 0028:M00F11NZ in GST SD(01) + " ;
2017-12-17 14:19:24 +01:00
const int head2w = al_get_text_width ( game - > _priv . font_bsod , header2 ) ;
2012-12-28 02:55:52 +01:00
2017-12-17 14:19:24 +01:00
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx , ( int ) ( offsety + 2 * fonth * 1.25 ) , ALLEGRO_ALIGN_CENTRE , header2 ) ;
2018-07-04 19:08:39 +02:00
al_draw_textf ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx - head2w / 2.0 , ( int ) ( offsety + 3 * fonth * 1.25 ) , ALLEGRO_ALIGN_LEFT , " %p and system just doesn't know what went wrong. " , game ) ;
2012-12-28 02:55:52 +01:00
2017-12-17 14:58:03 +01:00
const int error_len = strlen ( text ) ;
const int error_w = al_get_text_width ( game - > _priv . font_bsod , text ) ;
const int lines = ceil ( error_w / ( al_get_display_width ( game - > display ) * 0.8 ) ) ;
2018-01-20 02:44:33 +01:00
const int letters_per_line = ( error_len / lines ) + 1 ;
2018-01-20 02:45:13 +01:00
int row = 5 ;
for ( int l = 0 ; l < lines ; + + l ) {
2017-12-17 14:58:03 +01:00
int start = l * letters_per_line ;
2018-01-20 02:44:33 +01:00
unsigned int end = ( l + 1 ) * letters_per_line ;
if ( end > = sizeof ( text ) ) {
end = sizeof ( text ) - 1 ;
}
2017-12-17 14:58:03 +01:00
const char save_char = text [ end ] ;
text [ end ] = ' \0 ' ;
2018-01-20 02:44:33 +01:00
2018-07-04 19:08:39 +02:00
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx - ( error_w / ( float ) lines ) / 2.0 , ( int ) ( offsety + row + + * fonth * 1.25 ) , ALLEGRO_ALIGN_LEFT , text + start ) ;
2017-12-17 14:58:03 +01:00
text [ end ] = save_char ;
}
+ + row ;
2018-07-04 19:08:39 +02:00
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx - head2w / 2.0 , ( int ) ( offsety + row + + * fonth * 1.25 ) , ALLEGRO_ALIGN_LEFT , " * Press any key to terminate this error. " ) ;
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx - head2w / 2.0 , ( int ) ( offsety + row + + * fonth * 1.25 ) , ALLEGRO_ALIGN_LEFT , " * Press any key to destroy all muffins in the world. " ) ;
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx - head2w / 2.0 , ( int ) ( offsety + row + + * fonth * 1.25 ) , ALLEGRO_ALIGN_LEFT , " * Just kidding, please press any key anyway. " ) ;
2012-12-28 02:55:52 +01:00
2017-12-17 14:58:03 +01:00
+ + row ;
2018-01-20 02:44:33 +01:00
if ( exit ) {
2018-07-04 19:08:39 +02:00
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx - head2w / 2.0 , ( int ) ( offsety + row * fonth * 1.25 ) , ALLEGRO_ALIGN_LEFT , " This is fatal error. My bad. " ) ;
2012-12-28 02:55:52 +01:00
2018-01-20 02:44:33 +01:00
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx , ( int ) ( offsety + ( row + 2 ) * fonth * 1.25 ) , ALLEGRO_ALIGN_CENTRE , " Press any key to quit _ " ) ;
2012-12-28 02:55:52 +01:00
} else {
2018-07-04 19:08:39 +02:00
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx - head2w / 2.0 , ( int ) ( offsety + row * fonth * 1.25 ) , ALLEGRO_ALIGN_LEFT , " Anything I can do to help? " ) ;
2012-12-28 02:55:52 +01:00
2018-01-20 02:44:33 +01:00
al_draw_text ( game - > _priv . font_bsod , al_map_rgb ( 255 , 255 , 255 ) , offsetx , ( int ) ( offsety + ( row + 2 ) * fonth * 1.25 ) , ALLEGRO_ALIGN_CENTRE , " Press any key to continue _ " ) ;
2012-12-28 02:55:52 +01:00
}
al_flip_display ( ) ;
2012-12-28 03:08:36 +01:00
ALLEGRO_KEYBOARD_STATE kb ;
al_get_keyboard_state ( & kb ) ;
2017-09-10 21:35:14 +02:00
// FIXME: implement proper event loop there
2017-08-22 21:54:58 +02:00
# ifndef __EMSCRIPTEN__
2012-12-28 03:08:36 +01:00
int i ;
2017-09-10 21:35:14 +02:00
for ( i = 0 ; i < ALLEGRO_KEY_PAUSE ; i + + ) {
2012-12-28 03:08:36 +01:00
if ( al_key_down ( & kb , i ) ) {
done = true ;
break ;
}
}
2017-08-22 21:54:58 +02:00
# else
done = true ;
# endif
2012-12-28 02:55:52 +01:00
}
2018-12-15 00:59:34 +01:00
al_use_transform ( & game - > _priv . projection ) ;
2018-12-02 00:26:33 +01:00
# ifndef LIBSUPERDERPY_SINGLE_THREAD
2018-12-15 00:59:34 +01:00
if ( game - > _priv . loading . in_progress ) {
2018-12-02 00:26:33 +01:00
PrintConsole ( game , " Resuming the main thread... " ) ;
game - > _priv . in_bsod = false ;
al_signal_cond ( game - > _priv . bsod_cond ) ;
}
# endif
2016-06-27 21:20:02 +02:00
}
2018-03-15 00:46:52 +01:00
static void TestPath ( const char * filename , const char * subpath , char * * result ) {
2017-09-10 21:35:14 +02:00
if ( * result ) { return ; } //already found
ALLEGRO_PATH * tail = al_create_path ( filename ) ;
ALLEGRO_PATH * path = al_get_standard_path ( ALLEGRO_RESOURCES_PATH ) ;
ALLEGRO_PATH * data = al_create_path ( subpath ) ;
2016-06-27 21:20:02 +02:00
al_join_paths ( path , data ) ;
al_join_paths ( path , tail ) ;
//printf("Testing for %s\n", al_path_cstr(path, ALLEGRO_NATIVE_PATH_SEP));
if ( al_filename_exists ( al_path_cstr ( path , ALLEGRO_NATIVE_PATH_SEP ) ) ) {
* result = strdup ( al_path_cstr ( path , ALLEGRO_NATIVE_PATH_SEP ) ) ;
}
al_destroy_path ( tail ) ;
al_destroy_path ( data ) ;
al_destroy_path ( path ) ;
2012-12-28 02:55:52 +01:00
}
2018-03-15 00:46:52 +01:00
static char * TestDataFilePath ( struct Game * game , const char * filename ) {
2017-09-10 21:35:14 +02:00
char * result = NULL ;
2012-12-24 19:41:12 +01:00
2016-06-27 21:20:02 +02:00
TestPath ( filename , " data/ " , & result ) ;
2016-07-03 22:38:36 +02:00
TestPath ( filename , GetGameName ( game , " ../share/%s/data/ " ) , & result ) ;
2019-11-11 21:24:43 +01:00
TestPath ( filename , GetGameName ( game , " ../share/games/%s/data/ " ) , & result ) ;
2016-06-28 00:17:49 +02:00
2016-06-27 21:20:02 +02:00
# ifdef ALLEGRO_MACOSX
TestPath ( filename , " ../Resources/data/ " , & result ) ;
# endif
2012-12-24 19:41:12 +01:00
2018-12-13 03:56:23 +01:00
// build directories
2018-12-11 01:20:57 +01:00
TestPath ( filename , " ../../data/ " , & result ) ;
2018-12-13 03:56:23 +01:00
TestPath ( filename , " ../../../data/ " , & result ) ;
2018-12-11 01:20:57 +01:00
2017-09-20 18:13:49 +02:00
if ( result ) {
return result ;
}
// try current working directory if everything else fails
char origfn [ 255 ] = " data/ " ;
strncat ( origfn , filename , 249 ) ;
if ( al_filename_exists ( origfn ) ) {
return strdup ( origfn ) ;
}
return NULL ;
2017-08-08 00:06:37 +02:00
}
2019-03-29 20:53:45 +01:00
SYMBOL_EXPORT const char * FindDataFilePath ( struct Game * game , const char * filename ) {
2017-09-10 21:35:14 +02:00
char * result = 0 ;
2017-08-08 00:06:37 +02:00
# ifdef ALLEGRO_ANDROID
char origfn [ 255 ] = " android/ " ;
2017-10-30 21:17:47 +01:00
strncat ( origfn , filename , 246 ) ;
2017-08-08 00:06:37 +02:00
result = TestDataFilePath ( game , origfn ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
# endif
2018-12-18 13:19:16 +01:00
# ifdef MAEMO5
char origfn [ 255 ] = " maemo5/ " ;
2019-01-13 18:44:44 +01:00
strncat ( origfn , filename , 247 ) ;
result = TestDataFilePath ( game , origfn ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
# endif
2019-05-13 23:51:07 +02:00
# ifdef POCKETCHIP
char origfn [ 255 ] = " pocketchip/ " ;
strncat ( origfn , filename , 243 ) ;
result = TestDataFilePath ( game , origfn ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
# endif
2019-01-13 18:44:44 +01:00
# ifdef __EMSCRIPTEN__
char origfn [ 255 ] = " emscripten/ " ;
strncat ( origfn , filename , 243 ) ;
2018-12-18 13:19:16 +01:00
result = TestDataFilePath ( game , origfn ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
# endif
2019-04-29 03:11:55 +02:00
# ifdef LIBSUPERDERPY_FLACTOLOSSY_EXT
{
char * file = AddGarbage ( game , strdup ( filename ) ) ;
char * sub = strstr ( file , " .flac " ) ;
if ( sub ) {
sub [ 0 ] = ' . ' ;
sub [ 1 ] = LIBSUPERDERPY_FLACTOLOSSY_EXT [ 0 ] ;
sub [ 2 ] = LIBSUPERDERPY_FLACTOLOSSY_EXT [ 1 ] ;
sub [ 3 ] = LIBSUPERDERPY_FLACTOLOSSY_EXT [ 2 ] ;
sub [ 4 ] = LIBSUPERDERPY_FLACTOLOSSY_EXT [ 3 ] ;
sub [ 5 ] = 0 ;
}
result = TestDataFilePath ( game , file ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
2019-01-13 18:44:44 +01:00
}
2019-04-29 03:11:55 +02:00
# endif
2019-01-13 18:44:44 +01:00
2019-04-29 03:11:55 +02:00
# ifdef LIBSUPERDERPY_IMGTOWEBP
{
char * file = AddGarbage ( game , strdup ( filename ) ) ;
char * sub = strstr ( file , " .png " ) ;
if ( sub ) {
sub [ 0 ] = ' . ' ;
sub [ 1 ] = ' w ' ;
sub [ 2 ] = ' b ' ;
sub [ 3 ] = ' p ' ;
sub [ 4 ] = 0 ;
}
result = TestDataFilePath ( game , file ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
2019-01-13 18:44:44 +01:00
2019-04-29 03:11:55 +02:00
sub = strstr ( file , " .jpg " ) ;
if ( sub ) {
sub [ 0 ] = ' . ' ;
sub [ 1 ] = ' w ' ;
sub [ 2 ] = ' b ' ;
sub [ 3 ] = ' p ' ;
sub [ 4 ] = 0 ;
}
result = TestDataFilePath ( game , file ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
2019-01-13 18:44:44 +01:00
2019-04-29 03:11:55 +02:00
sub = strstr ( file , " .JPG " ) ;
if ( sub ) {
sub [ 0 ] = ' . ' ;
sub [ 1 ] = ' w ' ;
sub [ 2 ] = ' b ' ;
sub [ 3 ] = ' p ' ;
sub [ 4 ] = 0 ;
}
result = TestDataFilePath ( game , file ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
2019-01-13 18:44:44 +01:00
2019-04-29 03:11:55 +02:00
sub = strstr ( file , " .webp " ) ;
if ( sub ) {
sub [ 0 ] = ' . ' ;
sub [ 1 ] = ' w ' ;
sub [ 2 ] = ' b ' ;
sub [ 3 ] = ' p ' ;
sub [ 4 ] = 0 ;
}
result = TestDataFilePath ( game , file ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
}
2019-01-13 18:44:44 +01:00
}
2017-08-24 21:09:07 +02:00
# endif
2017-08-08 00:06:37 +02:00
result = TestDataFilePath ( game , filename ) ;
if ( result ) {
return AddGarbage ( game , result ) ;
2012-12-24 19:41:12 +01:00
}
2017-08-08 00:06:37 +02:00
2019-03-29 20:53:45 +01:00
return NULL ;
}
SYMBOL_EXPORT const char * GetDataFilePath ( struct Game * game , const char * filename ) {
const char * result = FindDataFilePath ( game , filename ) ;
if ( result ) {
return result ;
}
2017-08-08 00:06:37 +02:00
FatalError ( game , true , " Could not find data file: %s! " , filename ) ;
2017-08-22 21:54:58 +02:00
# ifdef __EMSCRIPTEN__
emscripten_exit_with_live_runtime ( ) ;
# endif
2017-08-08 00:06:37 +02:00
exit ( 1 ) ;
2012-12-24 19:41:12 +01:00
}
2018-11-30 04:20:35 +01:00
SYMBOL_EXPORT void PrintConsoleWithContext ( struct Game * game , int line , const char * file , const char * func , char * format , . . . ) {
2018-12-14 02:18:05 +01:00
al_lock_mutex ( game - > _priv . mutex ) ;
2012-12-24 19:41:12 +01:00
va_list vl ;
va_start ( vl , format ) ;
2017-09-09 00:11:43 +02:00
char * text = game - > _priv . console [ game - > _priv . console_pos ] ;
2017-09-10 21:35:14 +02:00
vsnprintf ( text , ( sizeof ( game - > _priv . console [ 0 ] ) / sizeof ( game - > _priv . console [ 0 ] [ 0 ] ) ) , format , vl ) ;
2012-12-24 19:41:12 +01:00
va_end ( vl ) ;
2017-09-10 22:33:32 +02:00
SUPPRESS_WARNING ( " -Wused-but-marked-unused " )
2018-01-21 03:18:51 +01:00
ALLEGRO_DEBUG ( " %s \n " , text ) ;
2017-09-10 22:33:32 +02:00
SUPPRESS_END
2018-11-02 06:07:49 +01:00
# if !defined(__EMSCRIPTEN__) && !defined(ALLEGRO_ANDROID)
2018-12-15 01:09:44 +01:00
if ( game - > config . debug . enabled )
2017-08-22 21:54:58 +02:00
# endif
{
2019-01-10 02:32:23 +01:00
# ifdef ALLEGRO_ANDROID
if ( game - > config . debug . verbose ) {
2019-01-17 02:42:33 +01:00
__android_log_print ( ANDROID_LOG_DEBUG , al_get_app_name ( ) , " %f %s:%d [%s] %s " , al_get_time ( ) , file , line , func , text ) ;
2019-01-10 02:32:23 +01:00
} else {
__android_log_print ( ANDROID_LOG_DEBUG , al_get_app_name ( ) , " [%s] %s " , func , text ) ;
}
# elif defined(__EMSCRIPTEN__)
if ( game - > config . debug . verbose ) {
2019-01-17 02:42:33 +01:00
emscripten_log ( EM_LOG_CONSOLE , " %f %s:%d [%s] %s " , al_get_time ( ) , file , line , func , text ) ;
2019-01-10 02:32:23 +01:00
} else {
emscripten_log ( EM_LOG_CONSOLE , " [%s] %s " , func , text ) ;
}
# else
2018-12-15 01:09:44 +01:00
if ( game - > config . debug . verbose ) {
2019-01-17 02:42:33 +01:00
printf ( " %f %s:%d " , al_get_time ( ) , file , line ) ;
2018-08-08 22:19:50 +02:00
}
printf ( " [%s] %s \n " , func , text ) ;
2017-08-22 21:54:58 +02:00
fflush ( stdout ) ;
2019-01-10 02:32:23 +01:00
# endif
2017-08-22 21:54:58 +02:00
}
2019-01-11 15:25:24 +01:00
2017-09-09 00:11:43 +02:00
game - > _priv . console_pos + + ;
2017-09-10 21:35:14 +02:00
if ( game - > _priv . console_pos > = ( sizeof ( game - > _priv . console ) / sizeof ( game - > _priv . console [ 0 ] ) ) ) {
2017-09-09 00:11:43 +02:00
game - > _priv . console_pos = 0 ;
}
2018-12-14 02:18:05 +01:00
al_unlock_mutex ( game - > _priv . mutex ) ;
2012-12-24 19:41:12 +01:00
}
2016-08-15 04:41:54 +02:00
2017-09-10 21:35:14 +02:00
SYMBOL_EXPORT void WindowCoordsToViewport ( struct Game * game , int * x , int * y ) {
2016-11-08 23:28:28 +01:00
int clipX , clipY , clipWidth , clipHeight ;
al_get_clipping_rectangle ( & clipX , & clipY , & clipWidth , & clipHeight ) ;
* x - = clipX ;
* y - = clipY ;
2018-10-10 21:37:54 +02:00
* x / = ( int ) ( clipWidth / ( float ) game - > viewport . width ) ;
* y / = ( int ) ( clipHeight / ( float ) game - > viewport . height ) ;
2016-11-08 23:28:28 +01:00
}
2017-05-07 01:30:22 +02:00
2018-02-03 03:46:33 +01:00
SYMBOL_EXPORT ALLEGRO_BITMAP * GetFramebuffer ( struct Game * game ) {
return game - > _priv . current_gamestate - > fb ;
}
SYMBOL_EXPORT void SetFramebufferAsTarget ( struct Game * game ) {
2018-11-22 03:40:41 +01:00
ALLEGRO_BITMAP * framebuffer = GetFramebuffer ( game ) ;
al_set_target_bitmap ( framebuffer ) ;
if ( framebuffer ! = al_get_backbuffer ( game - > display ) ) {
double x = al_get_bitmap_width ( framebuffer ) / ( double ) game - > viewport . width ;
double y = al_get_bitmap_height ( framebuffer ) / ( double ) game - > viewport . height ;
ALLEGRO_TRANSFORM t ;
al_identity_transform ( & t ) ;
al_scale_transform ( & t , x , y ) ;
al_use_transform ( & t ) ;
}
2018-02-03 03:46:33 +01:00
}
2019-02-26 01:59:49 +01:00
SYMBOL_EXPORT void SetClippingRectangle ( int x , int y , int width , int height ) {
ALLEGRO_TRANSFORM transform = * al_get_current_transform ( ) ;
float nx = x , ny = y , nx2 = x + width , ny2 = y + height ;
al_transform_coordinates ( & transform , & nx , & ny ) ;
al_transform_coordinates ( & transform , & nx2 , & ny2 ) ;
2019-03-26 04:31:24 +01:00
al_set_clipping_rectangle ( ( int ) nx , ( int ) ny , ( int ) ( nx2 - nx ) , ( int ) ( ny2 - ny ) ) ;
2019-02-26 01:59:49 +01:00
}
SYMBOL_EXPORT void ResetClippingRectangle ( void ) {
al_reset_clipping_rectangle ( ) ;
}
2019-02-26 18:14:19 +01:00
SYMBOL_EXPORT void PushTransform ( struct Game * game , ALLEGRO_TRANSFORM * t ) {
ALLEGRO_TRANSFORM transform = * t ;
if ( game - > _priv . transforms_no = = game - > _priv . transforms_alloc ) {
game - > _priv . transforms = realloc ( game - > _priv . transforms , sizeof ( ALLEGRO_TRANSFORM ) * + + game - > _priv . transforms_alloc ) ;
}
game - > _priv . transforms [ game - > _priv . transforms_no + + ] = * al_get_current_transform ( ) ;
al_compose_transform ( & transform , al_get_current_transform ( ) ) ;
al_use_transform ( & transform ) ;
}
SYMBOL_EXPORT void PopTransform ( struct Game * game ) {
al_use_transform ( & game - > _priv . transforms [ - - game - > _priv . transforms_no ] ) ;
}
2017-05-07 01:30:22 +02:00
SYMBOL_EXPORT ALLEGRO_BITMAP * CreateNotPreservedBitmap ( int width , int height ) {
int flags = al_get_new_bitmap_flags ( ) ;
2018-02-03 03:46:33 +01:00
//al_set_new_bitmap_depth(24);
2017-05-07 01:30:22 +02:00
al_add_new_bitmap_flag ( ALLEGRO_NO_PRESERVE_TEXTURE ) ;
2017-09-10 21:35:14 +02:00
ALLEGRO_BITMAP * bitmap = al_create_bitmap ( width , height ) ;
2017-05-07 01:30:22 +02:00
al_set_new_bitmap_flags ( flags ) ;
2018-02-03 03:46:33 +01:00
//al_set_new_bitmap_depth(0);
2017-05-07 01:30:22 +02:00
return bitmap ;
}
2018-02-03 03:46:33 +01:00
2019-03-05 03:20:06 +01:00
SYMBOL_EXPORT void EnableCompositor ( struct Game * game , void compositor ( struct Game * game ) ) {
2018-02-03 03:46:33 +01:00
PrintConsole ( game , " Compositor enabled. " ) ;
2018-12-15 00:59:34 +01:00
game - > _priv . params . handlers . compositor = compositor ? compositor : SimpleCompositor ;
2018-02-03 03:46:33 +01:00
ResizeGamestates ( game ) ;
}
SYMBOL_EXPORT void DisableCompositor ( struct Game * game ) {
PrintConsole ( game , " Compositor disabled. " ) ;
2018-12-15 00:59:34 +01:00
game - > _priv . params . handlers . compositor = NULL ;
2018-02-03 03:46:33 +01:00
ResizeGamestates ( game ) ;
}
2018-07-13 18:38:38 +02:00
2019-10-01 23:05:30 +02:00
SYMBOL_EXPORT char * StrToLower ( struct Game * game , const char * text ) {
2018-12-08 06:27:51 +01:00
// FIXME: UTF-8
2018-11-30 02:45:39 +01:00
char * res = strdup ( text ) , * iter = res ;
while ( * iter ) {
* iter = tolower ( * iter ) ;
iter + + ;
}
return AddGarbage ( game , res ) ;
}
2019-10-01 23:05:30 +02:00
SYMBOL_EXPORT char * StrToUpper ( struct Game * game , const char * text ) {
2018-12-08 06:27:51 +01:00
// FIXME: UTF-8
2018-11-30 02:45:39 +01:00
char * res = strdup ( text ) , * iter = res ;
while ( * iter ) {
* iter = toupper ( * iter ) ;
iter + + ;
}
return AddGarbage ( game , res ) ;
}
2019-10-01 23:05:30 +02:00
SYMBOL_EXPORT char * PunchNumber ( struct Game * game , const char * text , char ch , int number ) {
2018-07-13 18:38:38 +02:00
char * txt = strdup ( text ) ;
char * tmp = txt ;
while ( * tmp ) {
tmp + + ;
}
int num = 1 ;
while ( tmp ! = txt ) {
tmp - - ;
if ( * tmp = = ch ) {
2018-10-10 21:37:54 +02:00
* tmp = ' 0 ' + ( int ) floorf ( number / ( float ) num ) % 10 ;
2018-07-13 18:38:38 +02:00
num * = 10 ;
}
2019-03-29 20:53:45 +01:00
}
2018-07-13 18:38:38 +02:00
return AddGarbage ( game , txt ) ;
}
2018-07-31 21:25:07 +02:00
SYMBOL_EXPORT void QuitGame ( struct Game * game , bool allow_pausing ) {
# ifdef ALLEGRO_ANDROID
if ( allow_pausing ) {
JNIEnv * env = al_android_get_jni_env ( ) ;
jclass class_id = ( * env ) - > GetObjectClass ( env , al_android_get_activity ( ) ) ;
jmethodID method_id = ( * env ) - > GetMethodID ( env , class_id , " moveTaskToBack " ,
2019-03-29 03:24:36 +01:00
" (Z)Z " ) ;
2018-07-31 21:25:07 +02:00
jvalue jdata ;
jdata . z = JNI_TRUE ;
( * env ) - > CallBooleanMethodA ( env , al_android_get_activity ( ) , method_id , & jdata ) ;
return ;
}
# endif
UnloadAllGamestates ( game ) ;
}
2019-03-05 03:42:25 +01:00
SYMBOL_EXPORT bool ToggleFullscreen ( struct Game * game ) {
2019-12-09 02:02:50 +01:00
if ( IS_EMSCRIPTEN & & game - > _priv . params . fixed_size ) {
al_set_display_flag ( game - > display , ALLEGRO_FULLSCREEN_WINDOW , true ) ;
SetupViewport ( game ) ;
PrintConsole ( game , " Fullscreen toggled " ) ;
return true ;
}
2019-03-05 03:42:25 +01:00
game - > config . fullscreen = ! game - > config . fullscreen ;
if ( game - > config . fullscreen ) {
SetConfigOption ( game , " SuperDerpy " , " fullscreen " , " 1 " ) ;
} else {
SetConfigOption ( game , " SuperDerpy " , " fullscreen " , " 0 " ) ;
}
# ifdef ALLEGRO_ANDROID
al_set_display_flag ( game - > display , ALLEGRO_FRAMELESS , game - > config . fullscreen ) ;
# endif
al_set_display_flag ( game - > display , ALLEGRO_FULLSCREEN_WINDOW , game - > config . fullscreen ) ;
SetupViewport ( game ) ;
2019-09-06 22:57:04 +02:00
PrintConsole ( game , " Fullscreen toggled: %s " , game - > config . fullscreen ? " on " : " off " ) ;
2019-03-05 03:42:25 +01:00
return game - > config . fullscreen ;
}
SYMBOL_EXPORT bool ToggleMute ( struct Game * game ) {
game - > config . mute = ! game - > config . mute ;
al_set_mixer_gain ( game - > audio . mixer , game - > config . mute ? 0.0 : 1.0 ) ;
SetConfigOption ( game , " SuperDerpy " , " mute " , game - > config . mute ? " 1 " : " 0 " ) ;
PrintConsole ( game , " Mute: %d " , game - > config . mute ) ;
return game - > config . mute ;
}