mkxp/src/main.cpp

363 lines
8.2 KiB
C++

/*
** 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/>.
*/
#include <glew.h>
#include <alc.h>
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <SDL_sound.h>
#include <string>
#include "sharedstate.h"
#include "eventthread.h"
#include "debuglogger.h"
#include "debugwriter.h"
#include "exception.h"
#include "binding.h"
#include <unistd.h>
static void
rgssThreadError(RGSSThreadData *rtData, const std::string &msg)
{
rtData->rgssErrorMsg = msg;
rtData->ethread->requestTerminate();
rtData->rqTermAck = true;
}
static inline const char*
glGetStringInt(GLenum name)
{
return (const char*) glGetString(name);
}
static void
printGLInfo()
{
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);
}
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;
}
int rgssThreadFun(void *userdata)
{
RGSSThreadData *threadData = static_cast<RGSSThreadData*>(userdata);
SDL_Window *win = threadData->window;
SDL_GLContext glCtx;
/* 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);
glCtx = SDL_GL_CreateContext(win);
if (!glCtx)
{
rgssThreadError(threadData, std::string("Error creating context: ") + SDL_GetError());
return 0;
}
if (glewInit() != GLEW_OK)
{
rgssThreadError(threadData, "Error initializing glew");
SDL_GL_DeleteContext(glCtx);
return 0;
}
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(win);
printGLInfo();
/* Check for required GL version */
if (!GLEW_VERSION_2_0)
{
rgssThreadError(threadData, "At least OpenGL 2.0 is required");
SDL_GL_DeleteContext(glCtx);
return 0;
}
/* Setup optional GL extensions */
if (!setupOptionalGLExtensions(threadData))
{
SDL_GL_DeleteContext(glCtx);
return 0;
}
SDL_GL_SetSwapInterval(threadData->config.vsync ? 1 : 0);
DebugLogger dLogger;
/* Setup AL context */
ALCdevice *alcDev = alcOpenDevice(0);
if (!alcDev)
{
rgssThreadError(threadData, "Error opening OpenAL device");
SDL_GL_DeleteContext(glCtx);
return 0;
}
ALCcontext *alcCtx = alcCreateContext(alcDev, 0);
if (!alcCtx)
{
rgssThreadError(threadData, "Error creating OpenAL context");
alcCloseDevice(alcDev);
SDL_GL_DeleteContext(glCtx);
return 0;
}
alcMakeContextCurrent(alcCtx);
try
{
SharedState::initInstance(threadData);
}
catch (const Exception &exc)
{
rgssThreadError(threadData, exc.msg);
alcDestroyContext(alcCtx);
alcCloseDevice(alcDev);
SDL_GL_DeleteContext(glCtx);
return 0;
}
/* Start script execution */
scriptBinding->execute();
threadData->rqTermAck = true;
threadData->ethread->requestTerminate();
SharedState::finiInstance();
alcDestroyContext(alcCtx);
alcCloseDevice(alcDev);
SDL_GL_DeleteContext(glCtx);
return 0;
}
int main(int, char *argv[])
{
/* initialize SDL first */
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0)
{
Debug() << "Error initializing SDL:" << SDL_GetError();
return 0;
}
/* set working directory */
char *dataDir = SDL_GetBasePath();
if (dataDir)
{
int result = chdir(dataDir);
(void)result;
SDL_free(dataDir);
}
/* now we load the config */
Config conf;
conf.read();
conf.readGameINI();
int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;
if (IMG_Init(imgFlags) != imgFlags)
{
Debug() << "Error initializing SDL_image:" << SDL_GetError();
SDL_Quit();
return 0;
}
if (TTF_Init() < 0)
{
Debug() << "Error initializing SDL_ttf:" << SDL_GetError();
IMG_Quit();
SDL_Quit();
return 0;
}
if (Sound_Init() == 0)
{
Debug() << "Error initializing SDL_sound:" << Sound_GetError();
TTF_Quit();
IMG_Quit();
SDL_Quit();
return 0;
}
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;
win = SDL_CreateWindow(conf.game.title.c_str(),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
conf.defScreenW, conf.defScreenH, winFlags);
if (!win)
{
Debug() << "Error creating window";
return 0;
}
if (!conf.iconPath.empty())
{
SDL_Surface *iconImg = IMG_Load(conf.iconPath.c_str());
if (iconImg)
{
SDL_SetWindowIcon(win, iconImg);
SDL_FreeSurface(iconImg);
}
}
EventThread eventThread;
RGSSThreadData rtData(&eventThread, argv[0], win, conf);
/* 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)
{
Debug() << "RGSS thread ack'd request after" << i*10 << "ms";
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
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.c_str(),
"The RGSS script seems to be stuck and mkxp will now force quit", win);
if (!rtData.rgssErrorMsg.empty())
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.c_str(),
rtData.rgssErrorMsg.c_str(), win);
/* Clean up any remainin events */
eventThread.cleanup();
Debug() << "Shutting down.";
SDL_DestroyWindow(win);
Sound_Quit();
TTF_Quit();
IMG_Quit();
SDL_Quit();
return 0;
}