diff -ruN a/engine/include/video.h b/engine/include/video.h --- a/engine/include/video.h +++ b/engine/include/video.h @@ -432,6 +432,7 @@ extern void FreeGraphics(); +#ifndef __EMSCRIPTEN__ // ARB_texture_compression extern PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glCompressedTexImage3DARB; extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB; @@ -440,6 +441,7 @@ extern PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glCompressedTexSubImage2DARB; extern PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glCompressedTexSubImage1DARB; extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB; +#endif //@} diff -ruN a/engine/network/network_stub.cpp b/engine/network/network_stub.cpp --- a/engine/network/network_stub.cpp +++ b/engine/network/network_stub.cpp @@ -0,0 +1,58 @@ +/* +** Bos Wars in the browser has no network play. The sockets code (network/lowlevel.cpp, netconnect.cpp +** and network.cpp) is left out of the build and this file provides the few symbols the rest of the +** engine still refers to, as no-ops. IsNetworkGame() is always false (NetworkFildes is -1), so single +** player commands run directly. +** +** (c) fredygames.com, GPL-2.0-or-later like the rest of Bos Wars. +*/ +#include "stratagus.h" +#include "network.h" +#include "netconnect.h" + +int NetworkNumInterfaces; +Socket NetworkFildes = (Socket)-1; +int NetworkInSync = 1; +int NetworkUpdates = 5; +int NetworkLag = 10; +unsigned long NetworkStatus[PlayerMax]; + +std::string NetworkMapName; +int NetPlayers; +int NetworkPort = 6660; +int HostsCount; +CNetworkHost Hosts[PlayerMax]; +int NetConnectRunning; +NetworkState NetStates[PlayerMax]; +unsigned char NetLocalState; +int NetLocalHostsSlot; +int NetLocalPlayerNumber; +CServerSetup ServerSetupState; +CServerSetup LocalSetupState; + +void InitNetwork1(void) {} +void InitNetwork2(void) {} +void ExitNetwork1(void) {} +void NetworkEvent(void) {} +void NetworkSync(void) {} +void NetworkQuit(void) {} +void NetworkRecover(void) {} +void NetworkCommands(void) {} +void NetworkChatMessage(const std::string &) {} +void NetworkSendCommand(int, const CUnit *, int, int, const CUnit *, const CUnitType *, int) {} +void NetworkSendExtendedCommand(int, int, int, int, int, int) {} +void NetworkSendSelection(CUnit **, int) {} + +void NetworkServerStartGame(void) {} +void NetworkGamePrepareGameSettings(void) {} +void NetworkConnectSetupGame(void) {} +void NetworkInitClientConnect(void) {} +void NetworkExitClientConnect(void) {} +void NetworkInitServerConnect(int) {} +void NetworkExitServerConnect(void) {} +int NetworkParseSetupEvent(const unsigned char *, int) { return 0; } +int NetworkSetupServerAddress(const std::string &) { return -1; } +void NetworkProcessClientRequest(void) {} +void NetworkProcessServerRequest(void) {} +void NetworkServerResyncClients(void) {} +void NetworkDetachFromServer(void) {} diff -ruN a/engine/sdl1compat/gl_stub.c b/engine/sdl1compat/gl_stub.c --- a/engine/sdl1compat/gl_stub.c +++ b/engine/sdl1compat/gl_stub.c @@ -0,0 +1,16 @@ +/* +** The browser build always uses the software renderer, but the engine still contains the OpenGL +** immediate-mode drawing code. These no-ops satisfy the linker; they are never called. +*/ +#include + +void glVertex2i(GLint x, GLint y) {} +void glVertex2f(GLfloat x, GLfloat y) {} +void glColor4ub(GLubyte r, GLubyte g, GLubyte b, GLubyte a) {} +void glTexCoord2f(GLfloat s, GLfloat t) {} +void glEnd(void) {} +void glTexEnvf(GLenum target, GLenum pname, GLfloat param) {} +void glTranslatef(GLfloat x, GLfloat y, GLfloat z) {} +void glShadeModel(GLenum mode) {} +void glReadBuffer(GLenum mode) {} +void glOrtho(GLdouble l, GLdouble r, GLdouble b, GLdouble t, GLdouble n, GLdouble f) {} diff -ruN a/engine/sdl1compat/SDL_endian.h b/engine/sdl1compat/SDL_endian.h --- a/engine/sdl1compat/SDL_endian.h +++ b/engine/sdl1compat/SDL_endian.h @@ -0,0 +1 @@ +#include diff -ruN a/engine/sdl1compat/SDL_opengl.h b/engine/sdl1compat/SDL_opengl.h --- a/engine/sdl1compat/SDL_opengl.h +++ b/engine/sdl1compat/SDL_opengl.h @@ -0,0 +1,3 @@ +/* Only the declarations are needed: the browser build always uses the software renderer. */ +#include +#include diff -ruN a/engine/sdl1compat/SDL.h b/engine/sdl1compat/SDL.h --- a/engine/sdl1compat/SDL.h +++ b/engine/sdl1compat/SDL.h @@ -0,0 +1,285 @@ +/* +** sdl1compat: the small part of the SDL 1.2 API that Bos Wars uses, on top of SDL 2 (Emscripten port), +** written for the fredygames.com browser build. GPL-2.0-or-later, like the rest of Bos Wars. +** +** SDL 1.2 names that clash with SDL 2 types or values are redefined here after the SDL 2 headers; the code +** that talks to the real SDL 2 lives in sdl1compat.cpp, which does not include this file. +*/ +#ifndef BOSWARS_SDL1COMPAT_H +#define BOSWARS_SDL1COMPAT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---- video flags and surface helpers ---- */ +#define SDL_HWSURFACE 0x00000001 +#define SDL_HWPALETTE 0x20000000 +#define SDL_FULLSCREEN 0x80000000 +#define SDL_OPENGL 0x00000002 +#define SDL_SRCCOLORKEY 0x00001000 +#undef SDL_RLEACCEL +#define SDL_RLEACCEL 0x00004000 +#define SDL_SRCALPHA 0x00010000 +#define SDL_LOGPAL 0x01 +#define SDL_PHYSPAL 0x02 +#define SDL_APPMOUSEFOCUS 0x01 +#define SDL_APPINPUTFOCUS 0x02 +#define SDL_APPACTIVE 0x04 +#define SDL_BUTTON_WHEELUP 4 +#define SDL_BUTTON_WHEELDOWN 5 +#define SDL_ALPHA_OPAQUE 255 + +#undef SDL_SWSURFACE +#define SDL_SWSURFACE 0 + +/* Event types that do not exist in SDL 2 (values chosen by us, above SDL_USEREVENT). */ +#define SDL_ACTIVEEVENT (SDL_USEREVENT + 100) +#define SDL_VIDEORESIZE (SDL_USEREVENT + 101) +#define SDL_VIDEOEXPOSE (SDL_USEREVENT + 102) + +typedef struct { Uint32 type; Uint8 gain; Uint8 state; } BW_ActiveEvent; +typedef struct { Uint32 type; int w, h; } BW_ResizeEvent; +typedef struct { Uint32 type; } BW_ExposeEvent; + +typedef Sint32 SDLKey; +typedef Uint32 SDLMod; + +typedef struct { + Uint8 scancode; + SDLKey sym; + SDLMod mod; + Uint16 unicode; +} BW_keysym; + +typedef struct { + Uint32 type; + Uint8 which; + Uint8 state; + BW_keysym keysym; +} BW_KeyboardEvent; + +typedef union { + Uint32 type; + BW_ActiveEvent active; + BW_KeyboardEvent key; + SDL_MouseMotionEvent motion; + SDL_MouseButtonEvent button; + SDL_JoyAxisEvent jaxis; + SDL_JoyBallEvent jball; + SDL_JoyHatEvent jhat; + SDL_JoyButtonEvent jbutton; + BW_ResizeEvent resize; + BW_ExposeEvent expose; + SDL_QuitEvent quit; +} BW_Event; + +#define SDL_Event BW_Event +#define SDL_ActiveEvent BW_ActiveEvent +#define SDL_KeyboardEvent BW_KeyboardEvent +#define SDL_keysym BW_keysym +#define SDL_ResizeEvent BW_ResizeEvent +#define SDL_ExposeEvent BW_ExposeEvent + +/* SDL_Overlay is only used by the (disabled) Theora player. */ +typedef struct SDL_Overlay SDL_Overlay; + +int BW_PollEvent(BW_Event *event); +SDL_Surface *BW_SetVideoMode(int width, int height, int bpp, Uint32 flags); +int BW_VideoModeOK(int width, int height, int bpp, Uint32 flags); +void BW_Flip(SDL_Surface *screen); +void BW_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects); +void BW_WM_SetCaption(const char *title, const char *icon); +int BW_WM_ToggleFullScreen(SDL_Surface *screen); +int BW_WM_GrabInput(int mode); +int BW_EnableUNICODE(int enable); +void BW_WarpMouse(Uint16 x, Uint16 y); +int BW_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha); +int BW_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors); +int BW_SetPalette(SDL_Surface *surface, int flags, SDL_Color *colors, int firstcolor, int ncolors); +SDL_Surface *BW_DisplayFormat(SDL_Surface *surface); +SDL_Surface *BW_DisplayFormatAlpha(SDL_Surface *surface); +SDL_Surface *BW_AllocSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); +int BW_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key); +Uint32 BW_Ticks(void); +/* Surface state that SDL 1.2 kept in SDL_PixelFormat and SDL_Surface::flags. */ +int BW_HasColorKey(SDL_Surface *surface); +Uint32 BW_ColorKey(SDL_Surface *surface); +int BW_HasSurfaceAlpha(SDL_Surface *surface); +Uint8 BW_SurfaceAlpha(SDL_Surface *surface); +int BW_ScreenFullScreen(void); + +#ifdef __cplusplus +} +#endif + +#define SDL_PollEvent BW_PollEvent +#define SDL_SetVideoMode BW_SetVideoMode +#define SDL_VideoModeOK BW_VideoModeOK +#define SDL_Flip BW_Flip +#define SDL_UpdateRects BW_UpdateRects +#define SDL_WM_SetCaption BW_WM_SetCaption +#define SDL_WM_ToggleFullScreen BW_WM_ToggleFullScreen +#define SDL_WM_GrabInput BW_WM_GrabInput +#define SDL_EnableUNICODE BW_EnableUNICODE +#define SDL_WarpMouse BW_WarpMouse +#define SDL_SetAlpha BW_SetAlpha +#define SDL_SetColors BW_SetColors +#define SDL_SetPalette BW_SetPalette +#define SDL_DisplayFormat BW_DisplayFormat +#define SDL_DisplayFormatAlpha BW_DisplayFormatAlpha +#define SDL_AllocSurface BW_AllocSurface +#define SDL_SetColorKey BW_SetColorKey + +#define KMOD_META KMOD_GUI +#define SDL_GL_SwapBuffers() ((void)0) /* never reached: the browser build is software only */ + +#define SDL_GRAB_QUERY -1 +#define SDL_GRAB_OFF 0 +#define SDL_GRAB_ON 1 +#define SDL_DISABLE 0 +#define SDL_ENABLE 1 + +/* ---- SDL 1.2 key symbols ---- */ +#undef SDLK_UNKNOWN +#define SDLK_UNKNOWN 0 +#define SDLK_BACKSPACE 8 +#define SDLK_TAB 9 +#define SDLK_CLEAR 12 +#define SDLK_RETURN 13 +#define SDLK_PAUSE 19 +#define SDLK_ESCAPE 27 +#define SDLK_SPACE 32 +#define SDLK_EXCLAIM 33 +#define SDLK_QUOTEDBL 34 +#define SDLK_HASH 35 +#define SDLK_DOLLAR 36 +#define SDLK_AMPERSAND 38 +#define SDLK_QUOTE 39 +#define SDLK_LEFTPAREN 40 +#define SDLK_RIGHTPAREN 41 +#define SDLK_ASTERISK 42 +#define SDLK_PLUS 43 +#define SDLK_COMMA 44 +#define SDLK_MINUS 45 +#define SDLK_PERIOD 46 +#define SDLK_SLASH 47 +#define SDLK_0 48 +#define SDLK_1 49 +#define SDLK_2 50 +#define SDLK_3 51 +#define SDLK_4 52 +#define SDLK_5 53 +#define SDLK_6 54 +#define SDLK_7 55 +#define SDLK_8 56 +#define SDLK_9 57 +#define SDLK_COLON 58 +#define SDLK_SEMICOLON 59 +#define SDLK_LESS 60 +#define SDLK_EQUALS 61 +#define SDLK_GREATER 62 +#define SDLK_QUESTION 63 +#define SDLK_AT 64 +#define SDLK_LEFTBRACKET 91 +#define SDLK_BACKSLASH 92 +#define SDLK_RIGHTBRACKET 93 +#define SDLK_CARET 94 +#define SDLK_UNDERSCORE 95 +#define SDLK_BACKQUOTE 96 +#define SDLK_a 97 +#define SDLK_b 98 +#define SDLK_c 99 +#define SDLK_d 100 +#define SDLK_e 101 +#define SDLK_f 102 +#define SDLK_g 103 +#define SDLK_h 104 +#define SDLK_i 105 +#define SDLK_j 106 +#define SDLK_k 107 +#define SDLK_l 108 +#define SDLK_m 109 +#define SDLK_n 110 +#define SDLK_o 111 +#define SDLK_p 112 +#define SDLK_q 113 +#define SDLK_r 114 +#define SDLK_s 115 +#define SDLK_t 116 +#define SDLK_u 117 +#define SDLK_v 118 +#define SDLK_w 119 +#define SDLK_x 120 +#define SDLK_y 121 +#define SDLK_z 122 +#define SDLK_DELETE 127 +#define SDLK_KP0 256 +#define SDLK_KP1 257 +#define SDLK_KP2 258 +#define SDLK_KP3 259 +#define SDLK_KP4 260 +#define SDLK_KP5 261 +#define SDLK_KP6 262 +#define SDLK_KP7 263 +#define SDLK_KP8 264 +#define SDLK_KP9 265 +#define SDLK_KP_PERIOD 266 +#define SDLK_KP_DIVIDE 267 +#define SDLK_KP_MULTIPLY 268 +#define SDLK_KP_MINUS 269 +#define SDLK_KP_PLUS 270 +#define SDLK_KP_ENTER 271 +#define SDLK_KP_EQUALS 272 +#define SDLK_UP 273 +#define SDLK_DOWN 274 +#define SDLK_RIGHT 275 +#define SDLK_LEFT 276 +#define SDLK_INSERT 277 +#define SDLK_HOME 278 +#define SDLK_END 279 +#define SDLK_PAGEUP 280 +#define SDLK_PAGEDOWN 281 +#define SDLK_F1 282 +#define SDLK_F2 283 +#define SDLK_F3 284 +#define SDLK_F4 285 +#define SDLK_F5 286 +#define SDLK_F6 287 +#define SDLK_F7 288 +#define SDLK_F8 289 +#define SDLK_F9 290 +#define SDLK_F10 291 +#define SDLK_F11 292 +#define SDLK_F12 293 +#define SDLK_F13 294 +#define SDLK_F14 295 +#define SDLK_F15 296 +#define SDLK_NUMLOCK 300 +#define SDLK_CAPSLOCK 301 +#define SDLK_SCROLLOCK 302 +#define SDLK_RSHIFT 303 +#define SDLK_LSHIFT 304 +#define SDLK_RCTRL 305 +#define SDLK_LCTRL 306 +#define SDLK_RALT 307 +#define SDLK_LALT 308 +#define SDLK_RMETA 309 +#define SDLK_LMETA 310 +#define SDLK_LSUPER 311 +#define SDLK_RSUPER 312 +#define SDLK_MODE 313 +#define SDLK_COMPOSE 314 +#define SDLK_HELP 315 +#define SDLK_PRINT 316 +#define SDLK_SYSREQ 317 +#define SDLK_BREAK 318 +#define SDLK_MENU 319 +#define SDLK_POWER 320 +#define SDLK_EURO 321 +#define SDLK_UNDO 322 + +#endif diff -ruN a/engine/sdl1compat/sdl1compat.cpp b/engine/sdl1compat/sdl1compat.cpp --- a/engine/sdl1compat/sdl1compat.cpp +++ b/engine/sdl1compat/sdl1compat.cpp @@ -0,0 +1,423 @@ +/* +** sdl1compat: the small part of the SDL 1.2 API that Bos Wars uses, on top of SDL 2 (Emscripten port), +** written for the fredygames.com browser build. GPL-2.0-or-later, like the rest of Bos Wars. +** +** The screen is a 32 bit software surface (the engine's software renderer draws into it) that is +** uploaded to a streaming texture and presented whenever the engine flips or updates rectangles. +*/ +#include +#include +#include +#include + +/* Same layouts as in SDL.h (kept separate: this file only sees SDL 2). */ +extern "C" { +typedef struct { Uint32 type; Uint8 gain; Uint8 state; } BW_ActiveEvent; +typedef struct { Uint32 type; int w, h; } BW_ResizeEvent; +typedef struct { Uint32 type; } BW_ExposeEvent; +typedef struct { + Uint8 scancode; + Sint32 sym; + Uint32 mod; + Uint16 unicode; +} BW_keysym; +typedef struct { + Uint32 type; + Uint8 which; + Uint8 state; + BW_keysym keysym; +} BW_KeyboardEvent; +typedef union { + Uint32 type; + BW_ActiveEvent active; + BW_KeyboardEvent key; + SDL_MouseMotionEvent motion; + SDL_MouseButtonEvent button; + SDL_JoyAxisEvent jaxis; + SDL_JoyBallEvent jball; + SDL_JoyHatEvent jhat; + SDL_JoyButtonEvent jbutton; + BW_ResizeEvent resize; + BW_ExposeEvent expose; + SDL_QuitEvent quit; +} BW_Event; +} + +#define BW_ACTIVEEVENT (SDL_USEREVENT + 100) +#define BW_SRCALPHA 0x00010000 +#define BW_APPMOUSEFOCUS 0x01 +#define BW_APPINPUTFOCUS 0x02 +#define BW_APPACTIVE 0x04 +#define BW_FULLSCREEN 0x80000000u + +static SDL_Window *window; +static SDL_Renderer *renderer; +static SDL_Texture *texture; +static SDL_Surface *screen; +static bool wantUnicode; +static bool mouseGrabbed; + +extern "C" { + +Uint32 BW_Ticks(void) { return SDL_GetTicks(); } + +SDL_Surface *BW_SetVideoMode(int width, int height, int bpp, Uint32 flags) +{ + if (screen) { + SDL_FreeSurface(screen); + screen = NULL; + } + if (texture) { SDL_DestroyTexture(texture); texture = NULL; } + if (!window) { + window = SDL_CreateWindow("Bos Wars", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + width, height, 0); + if (!window) { + return NULL; + } + renderer = SDL_CreateRenderer(window, -1, 0); + if (!renderer) { + return NULL; + } + } else { + SDL_SetWindowSize(window, width, height); + } + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); + screen = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_ARGB8888); + if (!texture || !screen) { + return NULL; + } + SDL_FillRect(screen, NULL, 0); + return screen; +} + +int BW_VideoModeOK(int width, int height, int bpp, Uint32 flags) +{ + return 32; +} + +static void present(void) +{ + if (!screen || !texture) { + return; + } + SDL_UpdateTexture(texture, NULL, screen->pixels, screen->pitch); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); +} + +void BW_Flip(SDL_Surface *) +{ + present(); +} + +void BW_UpdateRects(SDL_Surface *, int, SDL_Rect *) +{ + present(); +} + +void BW_WM_SetCaption(const char *title, const char *) +{ + if (window) { + SDL_SetWindowTitle(window, title); + } +} + +int BW_WM_ToggleFullScreen(SDL_Surface *) +{ + return 0; +} + +int BW_WM_GrabInput(int mode) +{ + if (mode >= 0) { + mouseGrabbed = mode != 0; + } + return mouseGrabbed ? 1 : 0; +} + +int BW_EnableUNICODE(int enable) +{ + bool old = wantUnicode; + wantUnicode = enable != 0; + return old ? 1 : 0; +} + +void BW_WarpMouse(Uint16 x, Uint16 y) +{ + if (window) { + SDL_WarpMouseInWindow(window, x, y); + } +} + +int BW_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha) +{ + if (flag & BW_SRCALPHA) { + SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); + SDL_SetSurfaceAlphaMod(surface, alpha); + } else { + SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE); + SDL_SetSurfaceAlphaMod(surface, 255); + } + return 0; +} + +int BW_HasSurfaceAlpha(SDL_Surface *surface) +{ + SDL_BlendMode mode = SDL_BLENDMODE_NONE; + SDL_GetSurfaceBlendMode(surface, &mode); + return mode != SDL_BLENDMODE_NONE; +} + +Uint8 BW_SurfaceAlpha(SDL_Surface *surface) +{ + Uint8 a = 255; + SDL_GetSurfaceAlphaMod(surface, &a); + return a; +} + +int BW_HasColorKey(SDL_Surface *surface) +{ + return SDL_HasColorKey(surface) ? 1 : 0; +} + +Uint32 BW_ColorKey(SDL_Surface *surface) +{ + Uint32 key = 0; + SDL_GetColorKey(surface, &key); + return key; +} + +int BW_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key) +{ + return SDL_SetColorKey(surface, flag ? SDL_TRUE : SDL_FALSE, key); +} + +int BW_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors) +{ + return SDL_SetPaletteColors(surface->format->palette, colors, firstcolor, ncolors) == 0 ? 1 : 0; +} + +int BW_SetPalette(SDL_Surface *surface, int, SDL_Color *colors, int firstcolor, int ncolors) +{ + return BW_SetColors(surface, colors, firstcolor, ncolors); +} + +SDL_Surface *BW_DisplayFormat(SDL_Surface *surface) +{ + return SDL_ConvertSurface(surface, screen ? screen->format : surface->format, 0); +} + +SDL_Surface *BW_DisplayFormatAlpha(SDL_Surface *surface) +{ + return SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0); +} + +SDL_Surface *BW_AllocSurface(Uint32, int width, int height, int depth, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) +{ + return SDL_CreateRGBSurface(0, width, height, depth, Rmask, Gmask, Bmask, Amask); +} + +/* SDL 2 keycode -> SDL 1.2 keysym. */ +static Sint32 convertKey(SDL_Keycode k) +{ + if (k >= 0 && k < 127) { + if (k >= 'A' && k <= 'Z') return k + 32; + return k; + } + if (k == SDLK_DELETE) return 127; + switch (k) { + case SDLK_KP_0: return 256; + case SDLK_KP_1: return 257; + case SDLK_KP_2: return 258; + case SDLK_KP_3: return 259; + case SDLK_KP_4: return 260; + case SDLK_KP_5: return 261; + case SDLK_KP_6: return 262; + case SDLK_KP_7: return 263; + case SDLK_KP_8: return 264; + case SDLK_KP_9: return 265; + case SDLK_KP_PERIOD: return 266; + case SDLK_KP_DIVIDE: return 267; + case SDLK_KP_MULTIPLY: return 268; + case SDLK_KP_MINUS: return 269; + case SDLK_KP_PLUS: return 270; + case SDLK_KP_ENTER: return 271; + case SDLK_KP_EQUALS: return 272; + case SDLK_UP: return 273; + case SDLK_DOWN: return 274; + case SDLK_RIGHT: return 275; + case SDLK_LEFT: return 276; + case SDLK_INSERT: return 277; + case SDLK_HOME: return 278; + case SDLK_END: return 279; + case SDLK_PAGEUP: return 280; + case SDLK_PAGEDOWN: return 281; + case SDLK_F1: return 282; + case SDLK_F2: return 283; + case SDLK_F3: return 284; + case SDLK_F4: return 285; + case SDLK_F5: return 286; + case SDLK_F6: return 287; + case SDLK_F7: return 288; + case SDLK_F8: return 289; + case SDLK_F9: return 290; + case SDLK_F10: return 291; + case SDLK_F11: return 292; + case SDLK_F12: return 293; + case SDLK_F13: return 294; + case SDLK_F14: return 295; + case SDLK_F15: return 296; + case SDLK_NUMLOCKCLEAR: return 300; + case SDLK_CAPSLOCK: return 301; + case SDLK_SCROLLLOCK: return 302; + case SDLK_RSHIFT: return 303; + case SDLK_LSHIFT: return 304; + case SDLK_RCTRL: return 305; + case SDLK_LCTRL: return 306; + case SDLK_RALT: return 307; + case SDLK_LALT: return 308; + case SDLK_LGUI: return 311; + case SDLK_RGUI: return 312; + case SDLK_MODE: return 313; + case SDLK_HELP: return 315; + case SDLK_PRINTSCREEN: return 316; + case SDLK_SYSREQ: return 317; + case SDLK_PAUSE: return 19; + case SDLK_MENU: return 319; + case SDLK_POWER: return 320; + case SDLK_UNDO: return 322; + } + return 0; +} + +static Uint32 convertMod(Uint16 m) +{ + return m; /* only tested by the engine through the key symbols */ +} + +/* Mouse wheel turns into button 4/5 presses, as in SDL 1.2. */ +int BW_PollEvent(BW_Event *out) +{ + static int wheelPhase = 0; /* 0 none, 1 release pending */ + static int wheelButton = 0; + static int mx, my; + SDL_Event e; + + if (wheelPhase == 1) { + wheelPhase = 0; + memset(out, 0, sizeof(*out)); + out->button.type = SDL_MOUSEBUTTONUP; + out->button.button = wheelButton; + out->button.state = SDL_RELEASED; + out->button.x = mx; + out->button.y = my; + return 1; + } + + while (SDL_PollEvent(&e)) { + memset(out, 0, sizeof(*out)); + switch (e.type) { + case SDL_QUIT: + out->quit.type = SDL_QUIT; + return 1; + case SDL_KEYDOWN: + case SDL_KEYUP: { + if (e.key.repeat) { + continue; /* the engine does its own key repeat */ + } + Sint32 sym = convertKey(e.key.keysym.sym); + if (!sym) { + continue; + } + out->key.type = e.type; + out->key.state = e.key.state; + out->key.keysym.scancode = (Uint8)e.key.keysym.scancode; + out->key.keysym.sym = sym; + out->key.keysym.mod = convertMod(e.key.keysym.mod); + out->key.keysym.unicode = 0; + if (e.type == SDL_KEYDOWN && wantUnicode) { + /* The text for this key comes as a TEXTINPUT event right after it. */ + SDL_PumpEvents(); + SDL_Event peek[8]; + int n = SDL_PeepEvents(peek, 8, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_TEXTINPUT); + for (int i = 0; i < n; ++i) { + if (peek[i].type == SDL_KEYDOWN) { + break; + } + if (peek[i].type == SDL_TEXTINPUT) { + SDL_Event text; + SDL_PeepEvents(&text, 1, SDL_GETEVENT, SDL_TEXTINPUT, SDL_TEXTINPUT); + unsigned char c = (unsigned char)text.text.text[0]; + if (c >= 32 && c < 127) { + out->key.keysym.unicode = c; + } + break; + } + } + } + return 1; + } + case SDL_TEXTINPUT: + continue; + case SDL_MOUSEMOTION: + out->motion = e.motion; + mx = e.motion.x; + my = e.motion.y; + return 1; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + out->button = e.button; + mx = e.button.x; + my = e.button.y; + if (out->button.button > 3) { + continue; + } + return 1; + case SDL_MOUSEWHEEL: { + if (!e.wheel.y) { + continue; + } + int x, y; + SDL_GetMouseState(&x, &y); + mx = x; + my = y; + wheelButton = e.wheel.y > 0 ? 4 : 5; + wheelPhase = 1; + out->button.type = SDL_MOUSEBUTTONDOWN; + out->button.button = wheelButton; + out->button.state = SDL_PRESSED; + out->button.x = mx; + out->button.y = my; + return 1; + } + case SDL_WINDOWEVENT: + if (e.window.event == SDL_WINDOWEVENT_LEAVE || e.window.event == SDL_WINDOWEVENT_ENTER) { + out->active.type = BW_ACTIVEEVENT; + out->active.gain = e.window.event == SDL_WINDOWEVENT_ENTER; + out->active.state = BW_APPMOUSEFOCUS; + return 1; + } + if (e.window.event == SDL_WINDOWEVENT_HIDDEN || e.window.event == SDL_WINDOWEVENT_SHOWN) { + /* The tab went to the background (or came back): the engine pauses the game. */ + out->active.type = BW_ACTIVEEVENT; + out->active.gain = e.window.event == SDL_WINDOWEVENT_SHOWN; + out->active.state = BW_APPACTIVE; + return 1; + } + if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST || e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { + out->active.type = BW_ACTIVEEVENT; + out->active.gain = e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED; + out->active.state = BW_APPINPUTFOCUS; + return 1; + } + continue; + default: + continue; + } + } + return 0; +} + +} /* extern "C" */ diff -ruN a/engine/sound/ogg.cpp b/engine/sound/ogg.cpp --- a/engine/sound/ogg.cpp +++ b/engine/sound/ogg.cpp @@ -438,32 +438,54 @@ int total; ogg_page pg; ogg_sync_state sync; - int i; - // find total size + // find total size: the granule position of the last page. (The original searched backwards in + // steps of 4096 bytes and never ended for short sounds, which is all this game has.) pos = f->tell(); ogg_sync_init(&sync); - for (i = 0; ; ++i) { - f->seek(-1 * i * 4096, SEEK_END); + total = 0; + f->seek(0, SEEK_SET); + for (;;) { + int n; + buf = (unsigned char *)ogg_sync_buffer(&sync, 4096); - f->read(buf, 8192); - ogg_sync_wrote(&sync, 8192); - if (ogg_sync_pageout(&sync, &pg) == 1 && ogg_page_eos(&pg)) { - total = (int)(ogg_page_granulepos(&pg) * 2 * sample->Channels); + n = f->read(buf, 4096); + if (n <= 0) { break; } + ogg_sync_wrote(&sync, n); + while ((ret = ogg_sync_pageout(&sync, &pg)) != 0) { + if (ret == 1 && ogg_page_granulepos(&pg) > 0) { + total = (int)(ogg_page_granulepos(&pg) * 2 * sample->Channels); + } + } } + ogg_sync_clear(&sync); f->seek(pos, SEEK_SET); - sample->Buffer = new unsigned char[total]; + // Decode the whole sound. (The original read it through the streaming buffer into itself, which + // breaks for sounds longer than half of SOUND_BUFFER_SIZE.) The granule position can be a little + // short, so the buffer grows when needed. + int capacity = total + 65536; + sample->Buffer = new unsigned char[capacity]; pos = 0; - while ((ret = VorbisStreamRead(sample, &((CSampleVorbis *)sample)->Data, - sample->Buffer + pos, 8192))) { + for (;;) { + if (pos + 65536 > capacity) { + unsigned char *bigger = new unsigned char[capacity * 2]; + memcpy(bigger, sample->Buffer, pos); + delete[] sample->Buffer; + sample->Buffer = bigger; + capacity *= 2; + } + ret = VorbisProcessData(data, (char *)sample->Buffer + pos); + if (ret <= 0) { + break; + } pos += ret; } - sample->Len = total; + sample->Len = pos; sample->Pos = 0; f->close(); diff -ruN a/engine/video/font.cpp b/engine/video/font.cpp --- a/engine/video/font.cpp +++ b/engine/video/font.cpp @@ -890,7 +890,7 @@ CharWidth = new char[GlyphCount]; memset(CharWidth, 0, GlyphCount); CharWidth[0] = G->Width / 2; // a reasonable value for SPACE - ckey = G->Surface->format->colorkey; + ckey = BW_ColorKey(G->Surface); ipr = G->Surface->w / G->Width; SDL_LockSurface(G->Surface); diff -ruN a/engine/video/graphic.cpp b/engine/video/graphic.cpp --- a/engine/video/graphic.cpp +++ b/engine/video/graphic.cpp @@ -178,7 +178,7 @@ unsigned char alpha) const { if (!UseOpenGL) { - int oldalpha = Surface->format->alpha; + int oldalpha = BW_SurfaceAlpha(Surface); SDL_SetAlpha(Surface, SDL_SRCALPHA, alpha); DrawSub(gx, gy, w, h, x, y); SDL_SetAlpha(Surface, SDL_SRCALPHA, oldalpha); @@ -444,7 +444,7 @@ drect.x = x; drect.y = y; - oldalpha = Surface->format->alpha; + oldalpha = BW_SurfaceAlpha(Surface); SDL_SetAlpha(Surface, SDL_SRCALPHA, alpha); SDL_BlitSurface(SurfaceFlip, &srect, TheScreen, &drect); SDL_SetAlpha(Surface, SDL_SRCALPHA, oldalpha); @@ -480,7 +480,7 @@ drect.x = x; drect.y = y; - oldalpha = Surface->format->alpha; + oldalpha = BW_SurfaceAlpha(Surface); SDL_SetAlpha(Surface, SDL_SRCALPHA, alpha); SDL_BlitSurface(SurfaceFlip, &srect, TheScreen, &drect); SDL_SetAlpha(Surface, SDL_SRCALPHA, oldalpha); @@ -875,9 +875,9 @@ } s = SurfaceFlip = SDL_ConvertSurface(Surface, Surface->format, SDL_SWSURFACE); - if (Surface->flags & SDL_SRCCOLORKEY) { + if (BW_HasColorKey(Surface)) { SDL_SetColorKey(SurfaceFlip, SDL_SRCCOLORKEY | SDL_RLEACCEL, - Surface->format->colorkey); + BW_ColorKey(Surface)); } SDL_LockSurface(Surface); @@ -980,10 +980,10 @@ int maxw; int maxh; - useckey = g->Surface->flags & SDL_SRCCOLORKEY; + useckey = BW_HasColorKey(g->Surface); f = g->Surface->format; bpp = f->BytesPerPixel; - ckey = f->colorkey; + ckey = BW_ColorKey(g->Surface); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); maxw = std::min(g->GraphicWidth - ow, GLMaxTextureSize); @@ -992,8 +992,8 @@ h = PowerOf2(maxh); tex = new unsigned char[w * h * 4]; memset(tex, 0, w * h * 4); - if (g->Surface->flags & SDL_SRCALPHA) { - alpha = f->alpha; + if (BW_HasSurfaceAlpha(g->Surface)) { + alpha = BW_SurfaceAlpha(g->Surface); } else { alpha = 0xff; } @@ -1223,8 +1223,8 @@ SDL_UnlockSurface(Surface); memcpy(pal, Surface->format->palette->colors, sizeof(SDL_Color) * 256); - useckey = Surface->flags & SDL_SRCCOLORKEY; - ckey = Surface->format->colorkey; + useckey = BW_HasColorKey(Surface); + ckey = BW_ColorKey(Surface); SDL_FreeSurface(Surface); Surface = SDL_CreateRGBSurfaceFrom(data, w, h, 8, w, 0, 0, 0, 0); @@ -1324,7 +1324,7 @@ bool ret; bpp = Surface->format->BytesPerPixel; - if ((bpp == 1 && !(Surface->flags & SDL_SRCCOLORKEY)) || bpp == 3) { + if ((bpp == 1 && !(BW_HasColorKey(Surface))) || bpp == 3) { return false; } @@ -1332,7 +1332,7 @@ SDL_LockSurface(Surface); p = (unsigned char *)Surface->pixels + y * Surface->pitch + x * bpp; if (bpp == 1) { - if (*p == Surface->format->colorkey) { + if (*p == BW_ColorKey(Surface)) { ret = true; } } else { diff -ruN a/engine/video/sdl.cpp b/engine/video/sdl.cpp --- a/engine/video/sdl.cpp +++ b/engine/video/sdl.cpp @@ -101,6 +101,7 @@ -- Functions ----------------------------------------------------------------------------*/ +#ifndef __EMSCRIPTEN__ // ARB_texture_compression PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glCompressedTexImage3DARB; PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB; @@ -109,6 +110,7 @@ PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glCompressedTexSubImage2DARB; PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glCompressedTexSubImage1DARB; PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB; +#endif /*---------------------------------------------------------------------------- -- Sync @@ -183,6 +185,10 @@ /** ** Initialize OpenGL extensions */ +#ifdef __EMSCRIPTEN__ +// The browser build never uses OpenGL. +static void InitOpenGLExtensions() { GLTextureCompressionSupported = false; } +#else static void InitOpenGLExtensions() { // ARB_texture_compression @@ -216,6 +222,7 @@ } } } +#endif /** ** Initialize OpenGL diff -ruN a/scripts/boswars.lua b/scripts/boswars.lua --- a/scripts/boswars.lua +++ b/scripts/boswars.lua @@ -216,7 +216,7 @@ SetLocalPlayerName(preferences.PlayerName) end -UseOpenGL = preferences.UseOpenGL +UseOpenGL = false -- browser build: software renderer only SetVideoResolution(preferences.VideoWidth, preferences.VideoHeight) SetVideoFullScreen(preferences.VideoFullScreen) SetMaxOpenGLTexture(preferences.MaxOpenGLTexture) diff -ruN a/scripts/guichan.lua b/scripts/guichan.lua --- a/scripts/guichan.lua +++ b/scripts/guichan.lua @@ -813,8 +813,6 @@ end menu:addButton(_("Cancel (~)"), Video.Width / 2 - 250, Video.Height - 100, function() menu:stop() end) - menu:addButton(_("Start ~!Editor"), Video.Width / 2 + 50, Video.Height -100, - starteditorbutton) menu:run() PresentMap = OldPresentMap @@ -840,14 +838,9 @@ menu:addButton(_("~!Campaigns"), x2, ystep * 2, RunCampaignsMenu) menu:addButton(_("Show ~!Replay"), x2, ystep * 3, RunReplayMenu) - menu:addButton(_("~!MultiPlayer"), x1, ystep * 5, RunMultiPlayerMenu) - menu:addButton(_("~!Options"), x2, ystep * 5, function() RunOptionsMenu() menu:stop(1) end) - - menu:addButton(_("Cre~!dits"), x1, ystep * 6, RunCreditsMenu) - menu:addButton(_("Start ~!Editor"), x2, ystep * 6, RunEditorMenu) - - menu:addButton(_("E~!xit"), Video.Width / 2 - 100, Video.Height - 100, - function() menu:stop() end) + -- Browser build: no multiplayer, no map editor and no way to quit the program. + menu:addButton(_("~!Options"), x1, ystep * 5, function() RunOptionsMenu() menu:stop(1) end) + menu:addButton(_("Cre~!dits"), x2, ystep * 5, RunCreditsMenu) if false then menu:addButton("~!Widgets Demo", x2, ystep * 7, RunWidgetsMenu) diff -ruN a/scripts/menus/ingame/game.lua b/scripts/menus/ingame/game.lua --- a/scripts/menus/ingame/game.lua +++ b/scripts/menus/ingame/game.lua @@ -334,8 +334,6 @@ _("~!Surrender"), GameDefeat) end) menu:addButton(_("~!Quit to Menu"), 16, 40 + (36 * 2), RunQuitToMenuConfirmMenu) - menu:addButton(_("E~!xit Bos Wars"), 16, 40 + (36 * 3), - RunExitConfirmMenu) menu:addButton(_("Previous (~)"), 16, 248, function() menu:stop() end) @@ -354,7 +352,9 @@ menu:run(false) end +-- Browser build: the program cannot be closed from inside the page. function RunExitConfirmMenu() + do return end local menu = BosGameMenu() local height = 11 @@ -383,19 +383,15 @@ end function InitKeystrokes(keystrokes) - table.insert(keystrokes, {_("Alt-F"), _("- toggle full screen")}) - table.insert(keystrokes, {_("Alt-G"), _("- toggle grab mouse")}) table.insert(keystrokes, {_("Ctrl-S"), _("- mute sound")}) table.insert(keystrokes, {_("Ctrl-M"), _("- mute music")}) table.insert(keystrokes, {_("+"), _("- increase game speed")}) table.insert(keystrokes, {_("-"), _("- decrease game speed")}) table.insert(keystrokes, {_("Ctrl-P"), _("- pause game")}) table.insert(keystrokes, {_("PAUSE"), _("- pause game")}) - table.insert(keystrokes, {_("PRINT"), _("- make screen shot")}) table.insert(keystrokes, {_("Alt-H"), _("- help menu")}) table.insert(keystrokes, {_("Alt-R"), _("- restart game")}) table.insert(keystrokes, {_("Alt-Q"), _("- quit to main menu")}) - table.insert(keystrokes, {_("Alt-X"), _("- quit game")}) table.insert(keystrokes, {_("Alt-B"), _("- toggle expand map")}) table.insert(keystrokes, {_("Alt-M"), _("- game menu")}) table.insert(keystrokes, {_("ENTER"), _("- write a message")}) diff -ruN a/scripts/menus/options.lua b/scripts/menus/options.lua --- a/scripts/menus/options.lua +++ b/scripts/menus/options.lua @@ -185,15 +185,11 @@ { 800, 600 }, -- 4:3 { 1024, 768 }, -- 4:3 { 1280, 1024 }, -- 5:4 - { 1600, 1200 }, -- 4:3 -- 16:9, 8:5 (widescreen) aspect ratio modes { 1024, 600 }, -- 5.12:3 = 15.36:9 { 1280, 720 }, -- 16:9 { 1366, 768 }, -- 16:9 inexact - { 1440, 900 }, -- 8:5 - { 1680, 1050 }, -- 8:5 - { 1920, 1080 }, -- 16:9 - { 1920, 1200 } -- 8:5 + { 1440, 900 } -- 8:5 } local modes2 = math.ceil (#modes / 2); @@ -217,20 +213,7 @@ end end - fullScreen = menu:addCheckBox(_("Fullscreen"), x1, offybot + 36 * 1.5, - function() - ToggleFullScreen() - preferences.VideoFullScreen = Video.FullScreen - SavePreferences() - end) - fullScreen:setMarked(Video.FullScreen) - - useopengl = menu:addCheckBox(_("Use OpenGL (restart required)"), x1, offybot + 36 * 2.5, - function() - preferences.UseOpenGL = useopengl:isMarked() - SavePreferences() - end) - useopengl:setMarked(UseOpenGL) + -- Browser build: the page owns full screen, and the renderer is always the software one. end function RunVideoOptionsMenu(s)