2013-09-01 14:27:21 +00:00
/*
* * main . cpp
* *
* * This file is part of mkxp .
* *
* * Copyright ( C ) 2013 Jonas Kulla < Nyocurio @ gmail . com >
* *
* * mkxp 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 .
* *
* * mkxp 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 mkxp . If not , see < http : //www.gnu.org/licenses/>.
*/
2013-12-04 16:48:37 +00:00
# include <glew.h>
2013-11-30 11:00:11 +00:00
# include <alc.h>
2013-09-01 14:27:21 +00:00
2013-12-04 16:48:37 +00:00
# include <SDL.h>
# include <SDL_image.h>
# include <SDL_ttf.h>
2013-11-30 11:00:11 +00:00
# include <SDL_sound.h>
2013-09-01 14:27:21 +00:00
2013-12-11 19:46:54 +00:00
# include <string>
2013-10-09 10:30:33 +00:00
# include "sharedstate.h"
2013-09-01 14:27:21 +00:00
# include "eventthread.h"
# include "debuglogger.h"
2013-12-11 19:46:54 +00:00
# include "debugwriter.h"
2013-12-30 00:26:39 +00:00
# include "exception.h"
2013-09-01 14:27:21 +00:00
# include "binding.h"
static const char * reqExt [ ] =
{
2013-12-31 21:31:03 +00:00
// Everything we are using is CORE in OpenGL 2.0 except FBOs and VAOs which we'll handle in a special function
2013-09-02 09:12:45 +00:00
0
2013-09-01 14:27:21 +00:00
} ;
2013-12-31 21:31:03 +00:00
2013-10-15 17:35:03 +00:00
static void
2013-12-11 19:46:54 +00:00
rgssThreadError ( RGSSThreadData * rtData , const std : : string & msg )
2013-10-15 17:35:03 +00:00
{
rtData - > rgssErrorMsg = msg ;
rtData - > ethread - > requestTerminate ( ) ;
rtData - > rqTermAck = true ;
}
2013-10-15 21:19:52 +00:00
static inline const char *
glGetStringInt ( GLenum name )
{
return ( const char * ) glGetString ( name ) ;
}
static void
printGLInfo ( )
{
2013-12-11 19:46:54 +00:00
Debug ( ) < < " GL Vendor : " < < glGetStringInt ( GL_VENDOR ) ;
Debug ( ) < < " GL Renderer : " < < glGetStringInt ( GL_RENDERER ) ;
Debug ( ) < < " GL Version : " < < glGetStringInt ( GL_VERSION ) ;
Debug ( ) < < " GLSL Version : " < < glGetStringInt ( GL_SHADING_LANGUAGE_VERSION ) ;
2013-10-15 21:19:52 +00:00
}
2013-12-31 21:31:03 +00:00
static bool
setupOptionalGLExtensions ( RGSSThreadData * threadData )
{
if ( ! GLEW_ARB_framebuffer_object ) {
if ( ! GLEW_EXT_framebuffer_object & & ! GLEW_EXT_framebuffer_blit ) {
rgssThreadError ( threadData , " GL extensions \" GL_ARB_framebuffer_object \" or compatible extensiosns GL_EXT_framebuffer_object and GL_EXT_framebuffer_blit are not present " ) ;
return false ;
} else {
// setup compat
// From EXT_framebuffer_object
glGenRenderbuffers = glGenRenderbuffersEXT ;
glDeleteRenderbuffers = glDeleteRenderbuffersEXT ;
glBindRenderbuffer = glBindRenderbufferEXT ;
glRenderbufferStorage = glRenderbufferStorageEXT ;
glGenFramebuffers = glGenFramebuffersEXT ;
glDeleteFramebuffers = glDeleteFramebuffersEXT ;
glBindFramebuffer = glBindFramebufferEXT ;
glFramebufferTexture2D = glFramebufferTexture2DEXT ;
glFramebufferRenderbuffer = glFramebufferRenderbufferEXT ;
// From EXT_framebuffer_blit
glBlitFramebuffer = glBlitFramebufferEXT ;
}
}
if ( ! GLEW_ARB_timer_query & & GLEW_EXT_timer_query ) {
glGetQueryObjecti64v = glGetQueryObjecti64vEXT ;
glGetQueryObjectui64v = glGetQueryObjectui64vEXT ;
}
if ( ! GLEW_ARB_vertex_array_object ) {
if ( ! GLEW_APPLE_vertex_array_object ) {
rgssThreadError ( threadData , " GL extensions \" GL_ARB_vertex_array_object \" or compatible extensiosn GL_APPLE_vertex_array_object are not present " ) ;
return false ;
} else {
// setup compat
glBindVertexArray = glBindVertexArrayAPPLE ;
// the cast is because apple's uses const GLuint* and ARB doesn't
glGenVertexArrays = ( PFNGLGENVERTEXARRAYSPROC ) glGenVertexArraysAPPLE ;
glDeleteVertexArrays = glDeleteVertexArraysAPPLE ;
}
}
return true ;
}
2013-09-01 14:27:21 +00:00
int rgssThreadFun ( void * userdata )
{
RGSSThreadData * threadData = static_cast < RGSSThreadData * > ( userdata ) ;
SDL_Window * win = threadData - > window ;
2013-11-30 11:00:11 +00:00
SDL_GLContext glCtx ;
2013-09-01 14:27:21 +00:00
/* Setup GL context */
SDL_GL_SetAttribute ( SDL_GL_DOUBLEBUFFER , 1 ) ;
if ( threadData - > config . debugMode )
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_FLAGS , SDL_GL_CONTEXT_DEBUG_FLAG ) ;
2013-11-30 11:00:11 +00:00
glCtx = SDL_GL_CreateContext ( win ) ;
2013-09-01 14:27:21 +00:00
2013-11-30 11:00:11 +00:00
if ( ! glCtx )
2013-09-01 14:27:21 +00:00
{
2013-12-11 19:46:54 +00:00
rgssThreadError ( threadData , std : : string ( " Error creating context: " ) + SDL_GetError ( ) ) ;
2013-09-01 14:27:21 +00:00
return 0 ;
}
if ( glewInit ( ) ! = GLEW_OK )
{
2013-10-15 17:35:03 +00:00
rgssThreadError ( threadData , " Error initializing glew " ) ;
2013-11-30 11:00:11 +00:00
SDL_GL_DeleteContext ( glCtx ) ;
2013-12-30 00:26:39 +00:00
2013-09-01 14:27:21 +00:00
return 0 ;
}
2013-10-14 01:03:09 +00:00
glClearColor ( 0 , 0 , 0 , 1 ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
SDL_GL_SwapWindow ( win ) ;
2013-10-15 21:19:52 +00:00
printGLInfo ( ) ;
2013-10-15 17:35:03 +00:00
/* Check for required GL version */
if ( ! GLEW_VERSION_2_0 )
{
rgssThreadError ( threadData , " At least OpenGL 2.0 is required " ) ;
2013-11-30 11:00:11 +00:00
SDL_GL_DeleteContext ( glCtx ) ;
2013-12-30 00:26:39 +00:00
2013-10-15 17:35:03 +00:00
return 0 ;
}
2013-09-01 14:27:21 +00:00
/* Check for required GL extensions */
2013-09-04 09:31:35 +00:00
for ( int i = 0 ; reqExt [ i ] ; + + i )
2013-09-01 14:27:21 +00:00
{
2013-09-04 09:31:35 +00:00
if ( ! glewIsSupported ( reqExt [ i ] ) )
2013-09-01 14:27:21 +00:00
{
2013-12-11 19:46:54 +00:00
rgssThreadError ( threadData , std : : string ( " Required GL extension \" " )
2013-10-15 17:35:03 +00:00
+ reqExt [ i ] + " \" not present " ) ;
2013-11-30 11:00:11 +00:00
SDL_GL_DeleteContext ( glCtx ) ;
2013-12-30 00:26:39 +00:00
2013-09-01 14:27:21 +00:00
return 0 ;
}
}
2013-12-31 21:31:03 +00:00
/* Setup optional GL extensions */
if ( ! setupOptionalGLExtensions ( threadData ) ) {
SDL_GL_DeleteContext ( glCtx ) ;
return 0 ;
}
2013-09-01 14:27:21 +00:00
SDL_GL_SetSwapInterval ( threadData - > config . vsync ? 1 : 0 ) ;
DebugLogger dLogger ;
2013-11-30 11:00:11 +00:00
/* Setup AL context */
ALCdevice * alcDev = alcOpenDevice ( 0 ) ;
if ( ! alcDev )
{
rgssThreadError ( threadData , " Error opening OpenAL device " ) ;
SDL_GL_DeleteContext ( glCtx ) ;
2013-12-30 00:26:39 +00:00
2013-11-30 11:00:11 +00:00
return 0 ;
}
ALCcontext * alcCtx = alcCreateContext ( alcDev , 0 ) ;
if ( ! alcCtx )
{
rgssThreadError ( threadData , " Error creating OpenAL context " ) ;
alcCloseDevice ( alcDev ) ;
SDL_GL_DeleteContext ( glCtx ) ;
2013-12-30 00:26:39 +00:00
2013-11-30 11:00:11 +00:00
return 0 ;
}
alcMakeContextCurrent ( alcCtx ) ;
2013-12-30 00:26:39 +00:00
try
{
SharedState : : initInstance ( threadData ) ;
}
catch ( const Exception & exc )
{
rgssThreadError ( threadData , exc . msg ) ;
alcDestroyContext ( alcCtx ) ;
alcCloseDevice ( alcDev ) ;
SDL_GL_DeleteContext ( glCtx ) ;
return 0 ;
}
2013-09-01 14:27:21 +00:00
/* Start script execution */
scriptBinding - > execute ( ) ;
2013-09-22 22:02:28 +00:00
threadData - > rqTermAck = true ;
2013-09-01 14:27:21 +00:00
threadData - > ethread - > requestTerminate ( ) ;
2013-10-09 10:30:33 +00:00
SharedState : : finiInstance ( ) ;
2013-09-01 14:27:21 +00:00
2013-11-30 11:00:11 +00:00
alcDestroyContext ( alcCtx ) ;
alcCloseDevice ( alcDev ) ;
SDL_GL_DeleteContext ( glCtx ) ;
2013-09-01 14:27:21 +00:00
return 0 ;
}
int main ( int , char * argv [ ] )
{
2014-01-01 02:03:53 +00:00
// initialize SDL first
2013-10-13 21:07:40 +00:00
if ( SDL_Init ( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0 )
2013-09-01 14:27:21 +00:00
{
2013-12-11 19:46:54 +00:00
Debug ( ) < < " Error initializing SDL: " < < SDL_GetError ( ) ;
2014-01-01 02:03:53 +00:00
2013-09-01 14:27:21 +00:00
return 0 ;
}
2014-01-01 02:03:53 +00:00
// set working directory
char * dataDir = SDL_GetBasePath ( ) ;
if ( dataDir ) {
chdir ( dataDir ) ;
SDL_free ( dataDir ) ;
}
// now we load the config
Config conf ;
conf . read ( ) ;
conf . readGameINI ( ) ;
2013-10-02 11:50:58 +00:00
int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG ;
if ( IMG_Init ( imgFlags ) ! = imgFlags )
{
2013-12-11 19:46:54 +00:00
Debug ( ) < < " Error initializing SDL_image: " < < SDL_GetError ( ) ;
2013-10-02 11:50:58 +00:00
SDL_Quit ( ) ;
2013-11-30 11:00:11 +00:00
2013-10-02 11:50:58 +00:00
return 0 ;
}
if ( TTF_Init ( ) < 0 )
{
2013-12-11 19:46:54 +00:00
Debug ( ) < < " Error initializing SDL_ttf: " < < SDL_GetError ( ) ;
2013-10-02 11:50:58 +00:00
IMG_Quit ( ) ;
SDL_Quit ( ) ;
2013-11-30 11:00:11 +00:00
return 0 ;
}
if ( Sound_Init ( ) = = 0 )
{
2013-12-11 19:46:54 +00:00
Debug ( ) < < " Error initializing SDL_sound: " < < Sound_GetError ( ) ;
2013-11-30 11:00:11 +00:00
TTF_Quit ( ) ;
IMG_Quit ( ) ;
SDL_Quit ( ) ;
2013-10-02 11:50:58 +00:00
return 0 ;
}
2013-09-01 14:27:21 +00:00
SDL_SetHint ( " SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS " , " 0 " ) ;
SDL_Window * win ;
Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN ;
if ( conf . winResizable )
winFlags | = SDL_WINDOW_RESIZABLE ;
if ( conf . fullscreen )
winFlags | = SDL_WINDOW_FULLSCREEN_DESKTOP ;
2013-12-11 19:46:54 +00:00
win = SDL_CreateWindow ( conf . game . title . c_str ( ) ,
2013-09-01 14:27:21 +00:00
SDL_WINDOWPOS_UNDEFINED , SDL_WINDOWPOS_UNDEFINED ,
conf . defScreenW , conf . defScreenH , winFlags ) ;
if ( ! win )
{
2013-12-11 19:46:54 +00:00
Debug ( ) < < " Error creating window " ;
2013-09-01 14:27:21 +00:00
return 0 ;
}
EventThread eventThread ;
2013-12-31 22:23:15 +00:00
RGSSThreadData rtData ( & eventThread , argv [ 0 ] , win , conf ) ;
2013-09-01 14:27:21 +00:00
/* Start RGSS thread */
SDL_Thread * rgssThread =
SDL_CreateThread ( rgssThreadFun , " rgss " , & rtData ) ;
/* Start event processing */
eventThread . process ( rtData ) ;
/* Request RGSS thread to stop */
rtData . rqTerm = true ;
/* Wait for RGSS thread response */
for ( int i = 0 ; i < 1000 ; + + i )
{
/* We can stop waiting when the request was ack'd */
if ( rtData . rqTermAck )
{
2013-12-11 19:46:54 +00:00
Debug ( ) < < " RGSS thread ack'd request after " < < i * 10 < < " ms " ;
2013-09-01 14:27:21 +00:00
break ;
}
/* Give RGSS thread some time to respond */
SDL_Delay ( 10 ) ;
}
/* If RGSS thread ack'd request, wait for it to shutdown,
* otherwise abandon hope and just end the process as is . */
if ( rtData . rqTermAck )
SDL_WaitThread ( rgssThread , 0 ) ;
else
2013-12-11 19:46:54 +00:00
SDL_ShowSimpleMessageBox ( SDL_MESSAGEBOX_ERROR , conf . game . title . c_str ( ) ,
2013-09-01 14:27:21 +00:00
" The RGSS script seems to be stuck and mkxp will now force quit " , win ) ;
2013-12-11 19:46:54 +00:00
if ( ! rtData . rgssErrorMsg . empty ( ) )
SDL_ShowSimpleMessageBox ( SDL_MESSAGEBOX_ERROR , conf . game . title . c_str ( ) ,
rtData . rgssErrorMsg . c_str ( ) , win ) ;
2013-09-01 14:27:21 +00:00
/* Clean up any remainin events */
eventThread . cleanup ( ) ;
2013-12-11 19:46:54 +00:00
Debug ( ) < < " Shutting down. " ;
2013-09-01 14:27:21 +00:00
SDL_DestroyWindow ( win ) ;
2013-11-30 11:00:11 +00:00
Sound_Quit ( ) ;
2013-09-01 14:27:21 +00:00
TTF_Quit ( ) ;
IMG_Quit ( ) ;
SDL_Quit ( ) ;
return 0 ;
}