--- a/src/argParse.cpp +++ b/src/argParse.cpp @@ -23,30 +23,54 @@ * POSSIBILITY OF SUCH DAMAGE. */ +// fredygames: simple argument parser in place of Boost.Program_options. + #include "argParse.hpp" -#include using namespace std; -namespace po = boost::program_options; ArgParse::ArgParse(int argc, char* argv[]) { - // Declare the supported options. - desc.add_options() - ("file,f", po::value(), "read game variables from file or directory") - ("dir,d", po::value(), "load game data from separate directory") - ("write,w", po::value(), "write game variables to file") - ("sdlvideo,v", po::value(), "set the SDL_VIDEODRIVER environment variable") - ("help,h", "print this help message") - ; - - // First positional option is input file - // second is data dir, third is output file - po::positional_options_description p; - p.add("file", 1).add("dir", 1).add("write", 1); - - // Export to "vm" - po::store(po::command_line_parser(argc, argv) - .options(desc).positional(p).run(), vm); - po::notify(vm); + desc = + "Allowed options:\n" + " -f [ --file ] arg read game variables from file or directory\n" + " -d [ --dir ] arg load game data from separate directory\n" + " -w [ --write ] arg write game variables to file\n" + " -v [ --sdlvideo ] arg set the SDL_VIDEODRIVER environment variable\n" + " -h [ --help ] print this help message\n"; + + // Options with a value; the first positional arguments are the input + // file, the data directory and the output file, in that order. + static const char* const positional[] = { "file", "dir", "write" }; + size_t nextPositional = 0; + + for (int i = 1; i < argc; ++i) + { + string arg = argv[i]; + string name; + if (arg == "-h" || arg == "--help") + name = "help"; + else if (arg == "-f" || arg == "--file") + name = "file"; + else if (arg == "-d" || arg == "--dir") + name = "dir"; + else if (arg == "-w" || arg == "--write") + name = "write"; + else if (arg == "-v" || arg == "--sdlvideo") + name = "sdlvideo"; + + if (name == "help") + { + vm.values[name] = ""; + } + else if (!name.empty()) + { + if (i + 1 < argc) + vm.values[name] = argv[++i]; + } + else if (nextPositional < 3) + { + vm.values[positional[nextPositional++]] = arg; + } + } } --- a/src/argParse.hpp +++ b/src/argParse.hpp @@ -23,18 +23,40 @@ * POSSIBILITY OF SUCH DAMAGE. */ +// fredygames: simple argument parser in place of Boost.Program_options. + #ifndef ARG_PARSE_HPP #define ARG_PARSE_HPP -#include -#include "config.h" // Sets Dynamic boost linking +#include +#include class ArgParse { public: + struct Value + { + template T as() const { return value; } + std::string value; + }; + + struct VariablesMap + { + size_t count(const std::string& name) const { return values.count(name); } + Value operator[](const std::string& name) const + { + Value v; + std::map::const_iterator it = values.find(name); + if (it != values.end()) + v.value = it->second; + return v; + } + std::map values; + }; + ArgParse(int argc, char* argv[]); - boost::program_options::variables_map vm; - boost::program_options::options_description desc; + VariablesMap vm; + std::string desc; }; #endif // ARG_PARSE_HPP --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -266,7 +266,7 @@ char const* alt_name; }; -#if GVL_LINUX +#if GVL_LINUX || defined(__EMSCRIPTEN__) # define BOOST_HANDLE DIR * # define BOOST_INVALID_HANDLE_VALUE 0 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -211,6 +211,42 @@ clearKeys(); } +#ifdef __EMSCRIPTEN__ +#include +// fredygames: Asyncify only suspends on emscripten_sleep, not on the SDL_Delay +// alias, and the loops below must give the turn back to the browser. +#define SDL_Delay(ms) emscripten_sleep(ms) + +// Emscripten's SDL 1.2 has no SDL_WaitEvent; poll and sleep between tries. +static int fredygames_wait_event(SDL_Event* ev) +{ + while(!SDL_PollEvent(ev)) + SDL_Delay(10); + return 1; +} +#define SDL_WaitEvent fredygames_wait_event +#endif + +#ifdef __EMSCRIPTEN__ +SDL_Surface* fy_createScreen(int w, int h) +{ + SDL_Surface* surf = static_cast(std::calloc(1, sizeof(SDL_Surface))); + surf->format = static_cast(std::calloc(1, sizeof(SDL_PixelFormat))); + surf->format->palette = static_cast(std::calloc(1, sizeof(SDL_Palette))); + surf->format->palette->ncolors = 256; + surf->format->palette->colors = static_cast(std::calloc(256, sizeof(SDL_Color))); + surf->format->BitsPerPixel = 8; + surf->format->BytesPerPixel = 1; + surf->w = w; + surf->h = h; + surf->pitch = w; + surf->pixels = std::calloc(w * h, 1); + surf->clip_rect.w = w; + surf->clip_rect.h = h; + return surf; +} +#endif + void Gfx::init() { SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); @@ -223,7 +259,11 @@ SDL_ShowCursor(SDL_DISABLE); lastFrame = SDL_GetTicks(); +#ifdef __EMSCRIPTEN__ + screen = fy_createScreen(320, 200); +#else screen = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 200, 8, 0, 0, 0, 0); +#endif screenPixels = static_cast(screen->pixels); screenPitch = screen->pitch; @@ -241,6 +281,11 @@ const SDL_VideoInfo* vidInfo = SDL_GetVideoInfo(); desktopH = vidInfo->current_h; desktopW = vidInfo->current_w; +#ifdef __EMSCRIPTEN__ + // fredygames: the page scales the canvas; start at twice the original size + desktopW = 640; + desktopH = 400; +#endif } void Gfx::setVideoMode() @@ -248,6 +293,9 @@ int bitDepth = 8; if(settings->depth32) bitDepth = 32; +#ifdef __EMSCRIPTEN__ + bitDepth = 32; // fredygames: the browser canvas has no 8-bit mode +#endif int flags = SDL_SWSURFACE | SDL_RESIZABLE; if(fullscreen) @@ -274,6 +322,9 @@ int bitDepth = 8; if(settings->depth32) bitDepth = 32; +#ifdef __EMSCRIPTEN__ + bitDepth = 32; // fredygames: the browser canvas has no 8-bit mode +#endif int flags = SDL_SWSURFACE | SDL_RESIZABLE; @@ -536,8 +587,13 @@ if((ev.key.keysym.mod & KMOD_ALT) && s == SDLK_F4) running = false; +#ifdef __EMSCRIPTEN__ + // fredygames: full screen is the page's own button + if(false) +#else if(((ev.key.keysym.mod & KMOD_ALT) && s == SDLK_RETURN) || s == SDLK_F5) +#endif { fullscreen = !fullscreen; if(!fullscreen) @@ -829,6 +885,9 @@ std::size_t destPitch = back->pitch; std::size_t srcPitch = screenPitch; +#ifdef __EMSCRIPTEN__ + SDL_LockSurface(back); // fredygames: Emscripten copies the pixels to the canvas on unlock +#endif PalIdx* dest = reinterpret_cast(back->pixels) + offsetY * destPitch + offsetX * back->format->BytesPerPixel; PalIdx* src = screenPixels; @@ -1101,6 +1160,10 @@ } } +#ifdef __EMSCRIPTEN__ + if(screen != back) + SDL_UnlockSurface(back); +#endif //if(fullscreen) SDL_Flip(back); /*else --- a/src/gfx.hpp +++ b/src/gfx.hpp @@ -270,4 +270,90 @@ extern Gfx gfx; +#ifdef __EMSCRIPTEN__ +// fredygames: Emscripten's SDL 1.2 has no 8-bit surfaces, so the 320x200 +// indexed game screen is a surface made here: plain memory, a clip rectangle +// and a palette. These wrappers do for it what SDL_FillRect, SDL_SetClipRect, +// SDL_GetClipRect and SDL_SetColors do (as in SDL 1.2) and pass every other +// surface (the 32-bit window surface) to SDL. +SDL_Surface* fy_createScreen(int w, int h); + +inline bool fy_isScreen(SDL_Surface* surf) +{ + return surf == gfx.screen; +} + +inline int fy_FillRect(SDL_Surface* surf, SDL_Rect* rect, Uint32 color) +{ + if(!fy_isScreen(surf)) + return SDL_FillRect(surf, rect, color); + + int x1 = 0, y1 = 0, x2 = surf->w, y2 = surf->h; + if(rect) + { + x1 = rect->x; + y1 = rect->y; + x2 = x1 + rect->w; + y2 = y1 + rect->h; + SDL_Rect const& c = surf->clip_rect; + if(x1 < c.x) x1 = c.x; + if(y1 < c.y) y1 = c.y; + if(x2 > c.x + c.w) x2 = c.x + c.w; + if(y2 > c.y + c.h) y2 = c.y + c.h; + } + for(int y = y1; y < y2; ++y) + for(int x = x1; x < x2; ++x) + static_cast(surf->pixels)[y * surf->pitch + x] = static_cast(color); + return 0; +} + +inline SDL_bool fy_SetClipRect(SDL_Surface* surf, const SDL_Rect* rect) +{ + if(!fy_isScreen(surf)) + return SDL_SetClipRect(surf, rect); + + int x1 = 0, y1 = 0, x2 = surf->w, y2 = surf->h; + if(rect) + { + x1 = rect->x < 0 ? 0 : rect->x; + y1 = rect->y < 0 ? 0 : rect->y; + x2 = rect->x + rect->w > surf->w ? surf->w : rect->x + rect->w; + y2 = rect->y + rect->h > surf->h ? surf->h : rect->y + rect->h; + } + if(x2 <= x1 || y2 <= y1) + { + surf->clip_rect.x = surf->clip_rect.y = 0; + surf->clip_rect.w = surf->clip_rect.h = 0; + return rect ? SDL_FALSE : SDL_TRUE; + } + surf->clip_rect.x = x1; + surf->clip_rect.y = y1; + surf->clip_rect.w = x2 - x1; + surf->clip_rect.h = y2 - y1; + return SDL_TRUE; +} + +inline void fy_GetClipRect(SDL_Surface* surf, SDL_Rect* rect) +{ + if(fy_isScreen(surf)) + *rect = surf->clip_rect; + else + SDL_GetClipRect(surf, rect); +} + +inline int fy_SetColors(SDL_Surface* surf, SDL_Color* colors, int first, int n) +{ + if(!fy_isScreen(surf)) + return 1; // the 32-bit window surface has no palette + for(int i = 0; i < n; ++i) + surf->format->palette->colors[first + i] = colors[i]; + return 1; +} + +#define SDL_FillRect fy_FillRect +#define SDL_SetClipRect fy_SetClipRect +#define SDL_GetClipRect fy_GetClipRect +#define SDL_SetColors fy_SetColors +#endif + #endif // LIERO_GFX_HPP --- a/src/main.cpp +++ b/src/main.cpp @@ -28,7 +28,7 @@ #define UUID_DC1D9513CDD34960AB8A648004DA149D #include -#if !SDL13 +#if !SDL13 && !defined(__EMSCRIPTEN__) #include #endif @@ -62,6 +62,20 @@ //#undef main +#ifdef __EMSCRIPTEN__ +#include +// fredygames: the page calls this when the tab is hidden or closed, because +// the game only writes its options on exit. +extern "C" EMSCRIPTEN_KEEPALIVE void fredygames_save() +{ + if(gfx.settings) + { + gfx.saveSettings(); + gfx.saveOPT(lieroOPT); + } +} +#endif + int gameEntry(int argc, char* argv[]) try { @@ -84,6 +98,7 @@ return 0; } +#ifndef __EMSCRIPTEN__ if (argParse.vm.count("sdlvideo")) { // SDL_putenv seems to take char* in linux, STOOPID std::string s = "SDL_VIDEODRIVER=" + argParse.vm["sdlvideo"].as(); @@ -91,6 +106,7 @@ strcpy(sdlvd, s.c_str()); SDL_putenv(sdlvd); } +#endif if (argParse.vm.count("file")) inputFile = argParse.vm["file"].as(); @@ -136,8 +152,13 @@ #if !DISABLE_SOUND Console::write(common->S[Init_DSPVersion]); +#ifdef __EMSCRIPTEN__ + // fredygames: Emscripten's mixer has no Mix_Linked_Version + Console::write("1.2"); +#else SDL_version const* mixerVer = Mix_Linked_Version(); Console::write(toString(mixerVer->major) + "." + toString(mixerVer->minor)); +#endif Console::write(common->S[Init_Colon]); Console::write(common->S[Init_16bit]); Console::writeLine(common->S[Init_Autoinit]); --- a/src/sfx.cpp +++ b/src/sfx.cpp @@ -85,6 +85,9 @@ int count = readUint16(snd); sounds.resize(count); +#ifdef __EMSCRIPTEN__ + chunks.resize(count); +#endif long oldPos = ftell(snd); @@ -132,6 +135,9 @@ } *ptr++ = prev; +#ifdef __EMSCRIPTEN__ + chunks[i] = Mix_QuickLoad_RAW(buf, byteLength); +#endif #else for(int j = 0; j < length; ++j) { @@ -171,7 +177,11 @@ Console::writeWarning("Attempt to play non-existent sound"); return; } +#ifdef __EMSCRIPTEN__ + Mix_PlayChannel(channel, chunks[sound], loops); +#else Mix_PlayChannel(channel, &sounds[sound], loops); +#endif channelInfo[channel].id = id; #endif } --- a/src/sfx.hpp +++ b/src/sfx.hpp @@ -60,6 +60,9 @@ void stop(void* id); #if !DISABLE_SOUND std::vector sounds; +#ifdef __EMSCRIPTEN__ + std::vector chunks; // fredygames: Emscripten's mixer needs chunks made by Mix_QuickLoad_RAW +#endif ChannelInfo channelInfo[8]; #endif bool initialized;