2013-09-01 16:27:21 +02: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-11-30 12:00:11 +01:00
|
|
|
#include <alc.h>
|
2013-09-01 16:27:21 +02:00
|
|
|
|
2013-12-04 17:48:37 +01:00
|
|
|
#include <SDL.h>
|
|
|
|
#include <SDL_image.h>
|
|
|
|
#include <SDL_ttf.h>
|
2013-11-30 12:00:11 +01:00
|
|
|
#include <SDL_sound.h>
|
2013-09-01 16:27:21 +02:00
|
|
|
|
2014-11-29 17:23:42 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
2013-12-11 20:46:54 +01:00
|
|
|
#include <string>
|
|
|
|
|
2013-10-09 12:30:33 +02:00
|
|
|
#include "sharedstate.h"
|
2013-09-01 16:27:21 +02:00
|
|
|
#include "eventthread.h"
|
2014-11-29 17:47:40 +01:00
|
|
|
#include "gl-debug.h"
|
2013-12-11 20:46:54 +01:00
|
|
|
#include "debugwriter.h"
|
2013-12-30 01:26:39 +01:00
|
|
|
#include "exception.h"
|
2014-05-30 23:01:35 +02:00
|
|
|
#include "gl-fun.h"
|
2013-09-01 16:27:21 +02:00
|
|
|
|
|
|
|
#include "binding.h"
|
|
|
|
|
2017-03-03 22:45:31 -06:00
|
|
|
#ifdef __WINDOWS__
|
|
|
|
#include "resource.h"
|
|
|
|
#endif
|
2013-12-31 16:31:03 -05:00
|
|
|
|
2017-04-08 19:13:31 +02:00
|
|
|
#include "icon.png.xxd"
|
|
|
|
|
2013-10-15 19:35:03 +02:00
|
|
|
static void
|
2013-12-11 20:46:54 +01:00
|
|
|
rgssThreadError(RGSSThreadData *rtData, const std::string &msg)
|
2013-10-15 19:35:03 +02:00
|
|
|
{
|
|
|
|
rtData->rgssErrorMsg = msg;
|
|
|
|
rtData->ethread->requestTerminate();
|
2014-08-24 07:36:19 +02:00
|
|
|
rtData->rqTermAck.set();
|
2013-10-15 19:35:03 +02:00
|
|
|
}
|
|
|
|
|
2013-10-15 23:19:52 +02:00
|
|
|
static inline const char*
|
|
|
|
glGetStringInt(GLenum name)
|
|
|
|
{
|
2014-05-30 23:01:35 +02:00
|
|
|
return (const char*) gl.GetString(name);
|
2013-10-15 23:19:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
printGLInfo()
|
|
|
|
{
|
2013-12-11 20:46:54 +01: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 23:19:52 +02:00
|
|
|
}
|
|
|
|
|
2013-09-01 16:27:21 +02:00
|
|
|
int rgssThreadFun(void *userdata)
|
|
|
|
{
|
|
|
|
RGSSThreadData *threadData = static_cast<RGSSThreadData*>(userdata);
|
2015-01-03 18:48:09 +01:00
|
|
|
const Config &conf = threadData->config;
|
2013-09-01 16:27:21 +02:00
|
|
|
SDL_Window *win = threadData->window;
|
2013-11-30 12:00:11 +01:00
|
|
|
SDL_GLContext glCtx;
|
2013-09-01 16:27:21 +02:00
|
|
|
|
|
|
|
/* Setup GL context */
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
|
2015-01-03 18:48:09 +01:00
|
|
|
if (conf.debugMode)
|
2013-09-01 16:27:21 +02:00
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
|
|
|
|
2013-11-30 12:00:11 +01:00
|
|
|
glCtx = SDL_GL_CreateContext(win);
|
2013-09-01 16:27:21 +02:00
|
|
|
|
2013-11-30 12:00:11 +01:00
|
|
|
if (!glCtx)
|
2013-09-01 16:27:21 +02:00
|
|
|
{
|
2013-12-11 20:46:54 +01:00
|
|
|
rgssThreadError(threadData, std::string("Error creating context: ") + SDL_GetError());
|
2013-09-01 16:27:21 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-30 23:01:35 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
initGLFunctions();
|
|
|
|
}
|
|
|
|
catch (const Exception &exc)
|
2013-09-01 16:27:21 +02:00
|
|
|
{
|
2014-05-30 23:01:35 +02:00
|
|
|
rgssThreadError(threadData, exc.msg);
|
2013-11-30 12:00:11 +01:00
|
|
|
SDL_GL_DeleteContext(glCtx);
|
2013-12-30 01:26:39 +01:00
|
|
|
|
2013-09-01 16:27:21 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-23 14:32:11 +02:00
|
|
|
if (!conf.enableBlitting)
|
|
|
|
gl.BlitFramebuffer = 0;
|
|
|
|
|
2014-05-30 23:01:35 +02:00
|
|
|
gl.ClearColor(0, 0, 0, 1);
|
|
|
|
gl.Clear(GL_COLOR_BUFFER_BIT);
|
2013-10-14 03:03:09 +02:00
|
|
|
SDL_GL_SwapWindow(win);
|
|
|
|
|
2013-10-15 23:19:52 +02:00
|
|
|
printGLInfo();
|
|
|
|
|
2015-01-03 18:48:09 +01:00
|
|
|
bool vsync = conf.vsync || conf.syncToRefreshrate;
|
|
|
|
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
2013-09-01 16:27:21 +02:00
|
|
|
|
2014-11-29 17:47:40 +01:00
|
|
|
GLDebugLogger dLogger;
|
2013-09-01 16:27:21 +02:00
|
|
|
|
2013-11-30 12:00:11 +01:00
|
|
|
/* Setup AL context */
|
2014-12-23 20:32:16 +01:00
|
|
|
ALCcontext *alcCtx = alcCreateContext(threadData->alcDev, 0);
|
2013-11-30 12:00:11 +01:00
|
|
|
|
|
|
|
if (!alcCtx)
|
|
|
|
{
|
|
|
|
rgssThreadError(threadData, "Error creating OpenAL context");
|
|
|
|
SDL_GL_DeleteContext(glCtx);
|
2013-12-30 01:26:39 +01:00
|
|
|
|
2013-11-30 12:00:11 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
alcMakeContextCurrent(alcCtx);
|
|
|
|
|
2013-12-30 01:26:39 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
SharedState::initInstance(threadData);
|
|
|
|
}
|
|
|
|
catch (const Exception &exc)
|
|
|
|
{
|
|
|
|
rgssThreadError(threadData, exc.msg);
|
|
|
|
alcDestroyContext(alcCtx);
|
|
|
|
SDL_GL_DeleteContext(glCtx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-01 16:27:21 +02:00
|
|
|
|
|
|
|
/* Start script execution */
|
|
|
|
scriptBinding->execute();
|
|
|
|
|
2014-08-24 07:36:19 +02:00
|
|
|
threadData->rqTermAck.set();
|
2013-09-01 16:27:21 +02:00
|
|
|
threadData->ethread->requestTerminate();
|
|
|
|
|
2013-10-09 12:30:33 +02:00
|
|
|
SharedState::finiInstance();
|
2013-09-01 16:27:21 +02:00
|
|
|
|
2013-11-30 12:00:11 +01:00
|
|
|
alcDestroyContext(alcCtx);
|
|
|
|
SDL_GL_DeleteContext(glCtx);
|
2013-09-01 16:27:21 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-29 03:16:12 +02:00
|
|
|
static void printRgssVersion(int ver)
|
|
|
|
{
|
|
|
|
const char *const makers[] =
|
|
|
|
{ "", "XP", "VX", "VX Ace" };
|
|
|
|
|
|
|
|
char buf[128];
|
|
|
|
snprintf(buf, sizeof(buf), "RGSS version %d (%s)", ver, makers[ver]);
|
|
|
|
|
|
|
|
Debug() << buf;
|
|
|
|
}
|
|
|
|
|
2014-12-23 19:08:50 +01:00
|
|
|
static void showInitError(const std::string &msg)
|
|
|
|
{
|
|
|
|
Debug() << msg;
|
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "mkxp", msg.c_str(), 0);
|
|
|
|
}
|
|
|
|
|
2017-03-04 11:04:02 +01:00
|
|
|
static void setupWindowIcon(const Config &conf, SDL_Window *win)
|
|
|
|
{
|
|
|
|
SDL_RWops *iconSrc;
|
|
|
|
|
|
|
|
if (conf.iconPath.empty())
|
|
|
|
iconSrc = SDL_RWFromConstMem(assets_icon_png, assets_icon_png_len);
|
|
|
|
else
|
|
|
|
iconSrc = SDL_RWFromFile(conf.iconPath.c_str(), "rb");
|
|
|
|
|
|
|
|
SDL_Surface *iconImg = IMG_Load_RW(iconSrc, SDL_TRUE);
|
|
|
|
|
|
|
|
if (iconImg)
|
|
|
|
{
|
|
|
|
SDL_SetWindowIcon(win, iconImg);
|
|
|
|
SDL_FreeSurface(iconImg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-10 20:16:34 +01:00
|
|
|
int main(int argc, char *argv[])
|
2013-09-01 16:27:21 +02:00
|
|
|
{
|
2015-02-10 15:51:33 +01:00
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
|
|
|
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
|
|
|
|
|
2014-01-01 11:01:05 -05:00
|
|
|
/* initialize SDL first */
|
2013-10-13 23:07:40 +02:00
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0)
|
2013-09-01 16:27:21 +02:00
|
|
|
{
|
2014-12-23 19:08:50 +01:00
|
|
|
showInitError(std::string("Error initializing SDL: ") + SDL_GetError());
|
2013-09-01 16:27:21 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-02 02:22:10 +01:00
|
|
|
if (!EventThread::allocUserEvents())
|
|
|
|
{
|
2014-12-23 19:08:50 +01:00
|
|
|
showInitError("Error allocating SDL user events");
|
2014-01-02 02:22:10 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-02 09:30:17 +02:00
|
|
|
#ifndef WORKDIR_CURRENT
|
2014-01-01 11:01:05 -05:00
|
|
|
/* set working directory */
|
2013-12-31 21:03:53 -05:00
|
|
|
char *dataDir = SDL_GetBasePath();
|
2014-01-01 11:01:05 -05:00
|
|
|
if (dataDir)
|
|
|
|
{
|
|
|
|
int result = chdir(dataDir);
|
|
|
|
(void)result;
|
2013-12-31 21:03:53 -05:00
|
|
|
SDL_free(dataDir);
|
|
|
|
}
|
2014-08-02 09:30:17 +02:00
|
|
|
#endif
|
2013-12-31 21:03:53 -05:00
|
|
|
|
2014-01-01 11:01:05 -05:00
|
|
|
/* now we load the config */
|
2013-12-31 21:03:53 -05:00
|
|
|
Config conf;
|
2014-01-10 20:16:34 +01:00
|
|
|
conf.read(argc, argv);
|
2014-12-23 19:15:13 +01:00
|
|
|
|
|
|
|
if (!conf.gameFolder.empty())
|
|
|
|
if (chdir(conf.gameFolder.c_str()) != 0)
|
|
|
|
{
|
|
|
|
showInitError(std::string("Unable to switch into gameFolder ") + conf.gameFolder);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-31 21:03:53 -05:00
|
|
|
conf.readGameINI();
|
2017-08-08 16:07:43 +05:30
|
|
|
conf.readOverlayDesc();
|
2013-12-31 21:03:53 -05:00
|
|
|
|
2014-10-16 09:10:27 +02:00
|
|
|
assert(conf.rgssVersion >= 1 && conf.rgssVersion <= 3);
|
2014-08-29 03:16:12 +02:00
|
|
|
printRgssVersion(conf.rgssVersion);
|
|
|
|
|
2013-10-02 13:50:58 +02:00
|
|
|
int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;
|
|
|
|
if (IMG_Init(imgFlags) != imgFlags)
|
|
|
|
{
|
2014-12-23 19:08:50 +01:00
|
|
|
showInitError(std::string("Error initializing SDL_image: ") + SDL_GetError());
|
2013-10-02 13:50:58 +02:00
|
|
|
SDL_Quit();
|
2013-11-30 12:00:11 +01:00
|
|
|
|
2013-10-02 13:50:58 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TTF_Init() < 0)
|
|
|
|
{
|
2014-12-23 19:08:50 +01:00
|
|
|
showInitError(std::string("Error initializing SDL_ttf: ") + SDL_GetError());
|
2013-10-02 13:50:58 +02:00
|
|
|
IMG_Quit();
|
|
|
|
SDL_Quit();
|
2013-11-30 12:00:11 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Sound_Init() == 0)
|
|
|
|
{
|
2014-12-23 19:08:50 +01:00
|
|
|
showInitError(std::string("Error initializing SDL_sound: ") + Sound_GetError());
|
2013-11-30 12:00:11 +01:00
|
|
|
TTF_Quit();
|
|
|
|
IMG_Quit();
|
|
|
|
SDL_Quit();
|
|
|
|
|
2013-10-02 13:50:58 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2013-09-01 16:27:21 +02:00
|
|
|
|
|
|
|
SDL_Window *win;
|
2017-08-08 16:27:11 +05:30
|
|
|
|
|
|
|
/* Set the color space to 24 bit */
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
|
|
|
|
|
2014-01-25 09:24:55 +01:00
|
|
|
Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_FOCUS;
|
2013-09-01 16:27:21 +02:00
|
|
|
|
|
|
|
if (conf.winResizable)
|
|
|
|
winFlags |= SDL_WINDOW_RESIZABLE;
|
|
|
|
if (conf.fullscreen)
|
|
|
|
winFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
|
2013-12-11 20:46:54 +01:00
|
|
|
win = SDL_CreateWindow(conf.game.title.c_str(),
|
2013-09-01 16:27:21 +02:00
|
|
|
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
|
|
|
conf.defScreenW, conf.defScreenH, winFlags);
|
|
|
|
|
|
|
|
if (!win)
|
|
|
|
{
|
2014-12-23 19:08:50 +01:00
|
|
|
showInitError(std::string("Error creating window: ") + SDL_GetError());
|
2013-09-01 16:27:21 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-04 11:04:02 +01:00
|
|
|
/* OSX and Windows have their own native ways of
|
|
|
|
* dealing with icons; don't interfere with them */
|
|
|
|
#ifdef __LINUX__
|
|
|
|
setupWindowIcon(conf, win);
|
|
|
|
#else
|
|
|
|
(void) setupWindowIcon;
|
|
|
|
#endif
|
2014-01-01 16:52:07 -05:00
|
|
|
|
2014-12-23 20:32:16 +01:00
|
|
|
ALCdevice *alcDev = alcOpenDevice(0);
|
|
|
|
|
|
|
|
if (!alcDev)
|
|
|
|
{
|
|
|
|
showInitError("Error opening OpenAL device");
|
|
|
|
SDL_DestroyWindow(win);
|
|
|
|
TTF_Quit();
|
|
|
|
IMG_Quit();
|
|
|
|
SDL_Quit();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-03 18:48:09 +01:00
|
|
|
SDL_DisplayMode mode;
|
|
|
|
SDL_GetDisplayMode(0, 0, &mode);
|
|
|
|
|
|
|
|
/* Can't sync to display refresh rate if its value is unknown */
|
|
|
|
if (!mode.refresh_rate)
|
|
|
|
conf.syncToRefreshrate = false;
|
|
|
|
|
2013-09-01 16:27:21 +02:00
|
|
|
EventThread eventThread;
|
2014-12-23 20:32:16 +01:00
|
|
|
RGSSThreadData rtData(&eventThread, argv[0], win,
|
2015-01-03 18:48:09 +01:00
|
|
|
alcDev, mode.refresh_rate, conf);
|
2013-09-01 16:27:21 +02:00
|
|
|
|
2014-12-23 20:27:22 +01:00
|
|
|
int winW, winH;
|
|
|
|
SDL_GetWindowSize(win, &winW, &winH);
|
2015-01-02 01:41:23 +01:00
|
|
|
rtData.windowSizeMsg.post(Vec2i(winW, winH));
|
2014-12-23 20:27:22 +01:00
|
|
|
|
2014-01-25 09:24:55 +01:00
|
|
|
/* Load and post key bindings */
|
|
|
|
rtData.bindingUpdateMsg.post(loadBindings(conf));
|
|
|
|
|
2013-09-01 16:27:21 +02:00
|
|
|
/* Start RGSS thread */
|
|
|
|
SDL_Thread *rgssThread =
|
|
|
|
SDL_CreateThread(rgssThreadFun, "rgss", &rtData);
|
|
|
|
|
|
|
|
/* Start event processing */
|
|
|
|
eventThread.process(rtData);
|
|
|
|
|
|
|
|
/* Request RGSS thread to stop */
|
2014-08-24 07:36:19 +02:00
|
|
|
rtData.rqTerm.set();
|
2013-09-01 16:27:21 +02:00
|
|
|
|
|
|
|
/* 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 20:46:54 +01:00
|
|
|
Debug() << "RGSS thread ack'd request after" << i*10 << "ms";
|
2013-09-01 16:27:21 +02: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 20:46:54 +01:00
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.c_str(),
|
2013-09-01 16:27:21 +02:00
|
|
|
"The RGSS script seems to be stuck and mkxp will now force quit", win);
|
|
|
|
|
2013-12-11 20:46:54 +01:00
|
|
|
if (!rtData.rgssErrorMsg.empty())
|
2014-07-19 02:59:05 +02:00
|
|
|
{
|
|
|
|
Debug() << rtData.rgssErrorMsg;
|
2013-12-11 20:46:54 +01:00
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.c_str(),
|
|
|
|
rtData.rgssErrorMsg.c_str(), win);
|
2014-07-19 02:59:05 +02:00
|
|
|
}
|
2013-09-01 16:27:21 +02:00
|
|
|
|
|
|
|
/* Clean up any remainin events */
|
|
|
|
eventThread.cleanup();
|
|
|
|
|
2013-12-11 20:46:54 +01:00
|
|
|
Debug() << "Shutting down.";
|
2013-09-01 16:27:21 +02:00
|
|
|
|
2014-12-23 20:32:16 +01:00
|
|
|
alcCloseDevice(alcDev);
|
2013-09-01 16:27:21 +02:00
|
|
|
SDL_DestroyWindow(win);
|
|
|
|
|
2013-11-30 12:00:11 +01:00
|
|
|
Sound_Quit();
|
2013-09-01 16:27:21 +02:00
|
|
|
TTF_Quit();
|
|
|
|
IMG_Quit();
|
|
|
|
SDL_Quit();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|