From: Andreas Eversberg Date: Sun, 1 Apr 2018 12:12:04 +0000 (+0200) Subject: Add On screen display and help screen X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=mercenary-reloaded.git;a=commitdiff_plain;h=e66f60eb3551df9ebb500706a99243b528f7701f Add On screen display and help screen --- diff --git a/.gitignore b/.gitignore index e78ba40..3204ca3 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ src/libsound/libsound.a src/libjoystick/libjoystick.a src/libkeyboard/libkeyboard.a src/libdisk/libdisk.a +src/libtext/libtext.a src/mercenary/libmain.a src/mercenary/mercenary2 src/mercenary/mercenary3 diff --git a/configure.ac b/configure.ac index 656d293..a4306c0 100644 --- a/configure.ac +++ b/configure.ac @@ -59,6 +59,7 @@ AC_OUTPUT( src/libkeyboard/Makefile src/libdisk/Makefile src/libsdl/Makefile + src/libtext/Makefile src/mercenary/Makefile src/Makefile Makefile) diff --git a/src/Makefile.am b/src/Makefile.am index 5fe9513..0cc6d81 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,8 @@ SUBDIRS = \ libjoystick \ libkeyboard \ libdisk \ - libsound + libsound \ + libtext if HAVE_SDL2 if HAVE_GLEW diff --git a/src/libsdl/opengl.c b/src/libsdl/opengl.c index e10ef5f..74fcfe3 100644 --- a/src/libsdl/opengl.c +++ b/src/libsdl/opengl.c @@ -26,13 +26,18 @@ #include "opengl.h" #include +#define MAX_OSD 2 static uint8_t *legacy_rgb = NULL; static uint8_t *benson_rgb = NULL; +static uint8_t *osd_rgba[MAX_OSD] = { NULL, NULL }; static GLuint legacy_name; static GLuint benson_name; +static GLuint osd_name[MAX_OSD]; static int screen_width, screen_height; static int image_width, image_height; +static int osd_width[MAX_OSD], osd_height[MAX_OSD]; static int texture_size; +static int osd_size[MAX_OSD]; /* alloc and init an image texture with size that is greater than the power of two */ int init_opengl(int _image_width, int _image_height) @@ -82,6 +87,42 @@ error: return rc; } +/* alloc and init an osd texture with size that is greater than the power of two */ +int init_osd(int num, int _osd_width, int _osd_height) +{ + int rc; + + if (num < 0 || num >= MAX_OSD) { + print_error("given OSD number out of range"); + rc = -ENOMEM; + goto error; + } + + osd_width[num] = _osd_width; + osd_height[num] = _osd_height; + + /* generate texture */ + for (osd_size[num] = 1; osd_size[num] <= osd_width[num] || osd_size[num] <= osd_height[num]; osd_size[num] *= 2) + ; + osd_rgba[num] = calloc(osd_size[num] * osd_size[num], 4); + if (!osd_rgba[num]) { + print_error("Failed to allocate texture\n"); + rc = -ENOMEM; + goto error; + } + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */ + glGenTextures(1, &osd_name[num]); + glBindTexture(GL_TEXTURE_2D, osd_name[num]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, osd_size[num], osd_size[num], 0, GL_RGBA, GL_UNSIGNED_BYTE, osd_rgba[num]); + + return 0; + +error: + return rc; +} + /* set clip planes so that the image portion of the image texture is centered and pixels are rectengular */ void resize_opengl(int _screen_width, int _screen_height) { @@ -156,8 +197,8 @@ void opengl_blit_legacy(uint8_t *rgb, int filter) double height = (double)image_height / (double)texture_size; glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); /* no modulation with color */ glBindTexture(GL_TEXTURE_2D, legacy_name); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); /* no modulation with color */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height, GL_RGB, GL_UNSIGNED_BYTE, rgb); @@ -248,8 +289,8 @@ void opengl_blit_benson(uint8_t *rgb, int filter, int benson_at_line, double fov /* render benson */ rgb += image_width * benson_at_line * 3; glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); /* no modulation with color */ glBindTexture(GL_TEXTURE_2D, benson_name); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); /* no modulation with color */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, benson_at_line, image_width, image_height - benson_at_line, GL_RGB, GL_UNSIGNED_BYTE, rgb); @@ -266,6 +307,72 @@ void opengl_blit_benson(uint8_t *rgb, int filter, int benson_at_line, double fov glDisable(GL_TEXTURE_2D); } +/* render osd texture */ +void opengl_blit_osd(int num, uint8_t *rgba, int filter, int benson_at_line, double fov, double benson_size, double scale_x, double scale_y, double offset_x, double offset_y) +{ + double texture_left = 0.0; + double texture_right = (double)osd_width[num] / (double)osd_size[num]; + double texture_top = 0.0; + double texture_bottom = (double)osd_height[num] / (double)osd_size[num]; + double benson_start_at_position; + double osd_left = 0.0; + double osd_right = 1.0; + double osd_top = 0.0; + double osd_bottom = 1.0; + double slope, range, center; + if (fov) { + osd_left = -1.0; + osd_right = 1.0; + benson_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size; + osd_top = ((double)image_height - benson_start_at_position) / (double)image_width; + osd_bottom = -((double)image_height * 2.0 - ((double)image_height - benson_start_at_position)) / (double)image_width; + /* calculate field-of-view */ + slope = tan(fov / 360 * M_PI); + } + range = (osd_right - osd_left) / 2.0; + center = (osd_right + osd_left) / 2.0; + osd_left = (osd_left - center) * scale_x + center + range * offset_x; + osd_right = (osd_right - center) * scale_x + center + range * offset_x; + range = (osd_bottom - osd_top) / 2.0; + center = (osd_bottom + osd_top) / 2.0; + osd_top = (osd_top - center) * scale_y + center + range * offset_y; + osd_bottom = (osd_bottom - center) * scale_y + center + range * offset_y; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, osd_name[num]); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); /* no modulation with color */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, osd_width[num], osd_height[num], GL_RGBA, GL_UNSIGNED_BYTE, rgba); + glBegin(GL_QUADS); + if (fov) { + /* perspective viewport */ + glTexCoord2f(texture_left, texture_bottom); + glVertex3f(100.0 * osd_left, 100.0 * osd_bottom, -100.0 / slope); + glTexCoord2f(texture_right, texture_bottom); + glVertex3f(100.0 * osd_right, 100.0 * osd_bottom, -100.0 / slope); + glTexCoord2f(texture_right, texture_top); + glVertex3f(100.0 * osd_right, 100.0 * osd_top, -100.0 / slope); + glTexCoord2f(texture_left, texture_top); + glVertex3f(100.0 * osd_left, 100.0 * osd_top, -100.0 / slope); + } else { + /* orthogonal viewport */ + glTexCoord2f(texture_left, texture_top); + glVertex3f(osd_left, osd_top, 0.0); + glTexCoord2f(texture_right, texture_top); + glVertex3f(osd_right, osd_top, 0.0); + glTexCoord2f(texture_right, texture_bottom); + glVertex3f(osd_right, osd_bottom, 0.0); + glTexCoord2f(texture_left, texture_bottom); + glVertex3f(osd_left, osd_bottom, 0.0); + } + glEnd(); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +} + /* set color and opacity */ void opengl_render_color(double r, double g, double b, double a) { @@ -338,6 +445,8 @@ void opengl_render_point(double x, double y, double z, double size) /* free image texture */ void exit_opengl(void) { + int i; + if (legacy_rgb) { free(legacy_rgb); legacy_rgb = NULL; @@ -346,5 +455,11 @@ void exit_opengl(void) free(benson_rgb); benson_rgb = NULL; } + for (i = 0; i < MAX_OSD; i++) { + if (osd_rgba[i]) { + free(osd_rgba[i]); + osd_rgba[i] = NULL; + } + } } diff --git a/src/libsdl/opengl.h b/src/libsdl/opengl.h index 883c803..edfdc5f 100644 --- a/src/libsdl/opengl.h +++ b/src/libsdl/opengl.h @@ -1,5 +1,6 @@ int init_opengl(int _image_width, int _image_height); +int init_osd(int num, int _osd_width, int _osd_height); void resize_opengl(int _screen_width, int _screen_height); void opengl_copy_last(void); void opengl_clear(void); @@ -7,6 +8,7 @@ void opengl_viewport_legacy(int top); void opengl_blit_legacy(uint8_t *rgb, int filter); void opengl_viewport_improved(int bottom, int benson_at_line, double fov, double benson_size); void opengl_blit_benson(uint8_t *rgb, int filter, int benson_at_line, double fov, double benson_size, int pixel_size); +void opengl_blit_osd(int num, uint8_t *rgba, int filter, int benson_at_line, double fov, double benson_size, double scale_x, double scale_y, double offset_x, double offset_y); void opengl_render_color(double r, double g, double b, double a); void opengl_render_polygon(double *x, double *y, double *z, int count, int cull_face); void opengl_render_polygon_and_line(double *x, double *y, double *z, int count); diff --git a/src/libtext/Makefile.am b/src/libtext/Makefile.am new file mode 100644 index 0000000..528d3a5 --- /dev/null +++ b/src/libtext/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) + +noinst_LIBRARIES = libtext.a + +libtext_a_SOURCES = \ + text.c + diff --git a/src/libtext/text.c b/src/libtext/text.c new file mode 100644 index 0000000..6af9f21 --- /dev/null +++ b/src/libtext/text.c @@ -0,0 +1,124 @@ +/* Text screens and OSD generator + * + * (C) 2018 by Andreas Eversberg + * All Rights Reserved + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#include +#include +#include +#include "topaz.h" + +#define COLOR(c) { *rgba++ = palette_red[c]; *rgba++ = palette_green[c]; *rgba++ = palette_blue[c]; *rgba++ = palette_alpha[c]; } +#define COLOR_BLUE 0 +#define COLOR_WHITE 1 +#define COLOR_BLACK 2 +#define COLOR_YELLOW 3 +#define COLOR_RED 4 +static uint8_t palette_red[5] = { 0x00, 0xff, 0x00, 0xff, 0xdd }; +static uint8_t palette_green[5] = { 0x55, 0xff, 0x00, 0x88, 0x22 }; +static uint8_t palette_blue[5] = { 0xaa, 0xff, 0x22, 0x00, 0x22 }; +static uint8_t palette_alpha[5] = { 0xff, 0xff, 0xff, 0xff, 0xff }; + +uint8_t *text_alloc(int image_width, int image_height, uint8_t alpha) +{ + uint8_t *buffer, *rgba; + int x, y; + + palette_alpha[COLOR_BLUE] = alpha; + + buffer = calloc(image_width * image_height, 4); + if (!buffer) + return NULL; + + /* clear screen */ + rgba = buffer; + for (y = 0; y < image_height; y++) { + for (x = 0; x < image_width; x++) + COLOR(COLOR_BLUE) + } + + return buffer; +} + +/* render given text to an RGBA image */ +void text_render(uint8_t *buffer, int image_width, int image_height, const char *text, uint8_t alpha, int color, double start_column, double start_line, int double_height) +{ + uint8_t *rgba; + char *font; + int x, y, i, j; + + palette_alpha[COLOR_BLUE] = alpha; + + x = start_column * 8.0; + y = start_line * ((double_height) ? 16.0 : 8.0); + if (x < 0 || y < 0) + return; + + /* render text */ + while (*text) { + /* newline */ + if (*text == '\n') { + y += (double_height) ? 16 : 8; + x = start_column * 8.0; + text++; + continue; + } + /* if no space to the bottom */ + if (y + ((double_height) ? 16 : 8) > image_height) + break; + /* if no space to the right */ + if (x + 8 > image_width) { + text++; + continue; + } + /* if non visible characters */ + if (*text < 32 || *text > 126) { + text++; + continue; + } + /* set pointers */ + rgba = buffer + (y * image_width + x) * 4; + font = topaz_data + (*text - 32) * 64 + 7; + for (i = 0; i < 8; i++) { + /* render one line */ + for (j = 0; j < 8; j++) { + if (*font) + COLOR(color) + else + COLOR(COLOR_BLUE) + font += 8; + } + if (double_height) { + font -= 64; + rgba += (image_width - 8) * 4; + /* render line again */ + for (j = 0; j < 8; j++) { + if (*font) + COLOR(color) + else + COLOR(COLOR_BLUE) + font += 8; + } + } + rgba += (image_width - 8) * 4; + font -= 65; + } + x += 8; + text++; + } +} + diff --git a/src/libtext/text.h b/src/libtext/text.h new file mode 100644 index 0000000..f3be18e --- /dev/null +++ b/src/libtext/text.h @@ -0,0 +1,4 @@ + +uint8_t *text_alloc(int image_width, int image_height, uint8_t alpha); +void text_render(uint8_t *buffer, int image_width, int image_height, const char *text, uint8_t alpha, int color, double start_column, double start_line, int double_height); + diff --git a/src/libtext/topaz.h b/src/libtext/topaz.h new file mode 100644 index 0000000..ff92ce8 --- /dev/null +++ b/src/libtext/topaz.h @@ -0,0 +1,770 @@ +static char topaz_data[] = { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,1,0, + 0,1,0,1,1,1,1,1, + 0,1,0,1,1,1,1,1, + 0,0,0,0,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,1,0,1,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,1,0,1,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,1,0,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,0,0,1,0,0, + 0,0,1,0,1,1,1,0, + 0,1,1,0,1,0,1,1, + 0,1,1,0,1,0,1,1, + 0,0,1,1,1,0,1,0, + 0,0,0,1,0,0,1,0, + 0,0,0,0,0,0,0,0, + 1,1,0,0,0,1,1,0, + 0,1,1,0,0,1,1,0, + 0,0,1,1,0,0,0,0, + 0,0,0,1,1,0,0,0, + 0,0,0,0,1,1,0,0, + 0,1,1,0,0,1,1,0, + 0,1,1,0,0,0,1,1, + 0,0,0,0,0,0,0,1, + 0,0,1,1,0,0,0,0, + 0,1,1,1,1,1,1,0, + 0,1,0,0,1,1,1,1, + 0,1,0,1,1,0,0,1, + 0,0,1,1,0,1,1,1, + 0,1,1,1,1,0,1,0, + 0,1,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,0,0, + 0,0,0,0,0,1,1,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,0,0,0,1,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,1,0,0,0,1,1, + 0,0,1,1,1,1,1,0, + 0,0,0,1,1,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,1,0,1,0,1,0, + 0,0,1,1,1,1,1,0, + 0,0,0,1,1,1,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,1,1,1,0, + 0,0,1,0,1,0,1,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,1,1,1,1,1,0, + 0,0,1,1,1,1,1,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,1,1,0,0,0,0,0, + 0,1,1,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,1,0,0,0,0,0, + 0,1,1,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 1,1,0,0,0,0,0,0, + 0,1,1,0,0,0,0,0, + 0,0,1,1,0,0,0,0, + 0,0,0,1,1,0,0,0, + 0,0,0,0,1,1,0,0, + 0,0,0,0,0,1,1,0, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,0,1, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,1,1,1,1,1, + 0,1,0,1,1,0,0,1, + 0,1,0,0,1,1,0,1, + 0,1,1,1,1,1,1,1, + 0,0,1,1,1,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 0,1,0,0,0,0,1,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,1,0,0,0,1,0, + 0,1,1,1,0,0,1,1, + 0,1,0,1,1,0,0,1, + 0,1,0,0,1,0,0,1, + 0,1,1,0,1,1,1,1, + 0,1,1,0,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,0,0,0,1,0, + 0,1,1,0,0,0,1,1, + 0,1,0,0,1,0,0,1, + 0,1,0,0,1,0,0,1, + 0,1,1,1,1,1,1,1, + 0,0,1,1,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,0,0,0, + 0,0,0,1,1,1,0,0, + 0,0,0,1,0,1,1,0, + 0,1,0,1,0,0,1,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,1,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,0,0,1,1,1, + 0,1,1,0,0,1,1,1, + 0,1,0,0,0,1,0,1, + 0,1,0,0,0,1,0,1, + 0,1,1,1,1,1,0,1, + 0,0,1,1,1,0,0,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,1,0,0, + 0,1,1,1,1,1,1,0, + 0,1,0,0,1,0,1,1, + 0,1,0,0,1,0,0,1, + 0,1,1,1,1,0,0,1, + 0,0,1,1,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,1,1, + 0,1,1,1,0,0,0,1, + 0,1,1,1,1,0,0,1, + 0,0,0,0,1,1,1,1, + 0,0,0,0,0,1,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,0,1,1,0, + 0,1,1,1,1,1,1,1, + 0,1,0,0,1,0,0,1, + 0,1,0,0,1,0,0,1, + 0,1,1,1,1,1,1,1, + 0,0,1,1,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,1,0, + 0,1,0,0,1,1,1,1, + 0,1,0,0,1,0,0,1, + 0,1,1,0,1,0,0,1, + 0,0,1,1,1,1,1,1, + 0,0,0,1,1,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,1,0,0,1,1,0, + 0,1,1,0,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,1,1,0,0,1,1,0, + 0,1,1,0,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,0,1,1,0, + 0,1,1,0,0,0,1,1, + 0,1,0,0,0,0,0,1, + 0,0,0,1,0,1,0,0, + 0,0,0,1,0,1,0,0, + 0,0,0,1,0,1,0,0, + 0,0,0,1,0,1,0,0, + 0,0,0,1,0,1,0,0, + 0,0,0,1,0,1,0,0, + 0,0,0,1,0,1,0,0, + 0,0,0,1,0,1,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,0,0,0,1,1, + 0,0,1,1,0,1,1,0, + 0,0,0,1,1,1,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,0, + 0,0,0,0,0,0,1,1, + 0,1,0,1,0,0,0,1, + 0,1,0,1,1,0,0,1, + 0,0,0,0,1,1,1,1, + 0,0,0,0,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,1, + 0,1,0,1,1,1,0,1, + 0,1,0,1,1,1,0,1, + 0,0,0,1,1,1,1,1, + 0,0,0,1,1,1,1,0, + 0,0,0,0,0,0,0,0, + 0,1,1,0,0,0,0,0, + 0,1,1,1,1,0,0,0, + 0,0,0,1,1,1,1,0, + 0,0,0,1,0,1,1,1, + 0,0,0,1,0,1,1,1, + 0,0,0,1,1,1,1,0, + 0,1,1,1,1,0,0,0, + 0,1,1,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,1,0,0,1, + 0,1,0,0,1,0,0,1, + 0,1,1,1,1,1,1,1, + 0,0,1,1,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,0,0,0,1,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,1,0,0,0,1,1, + 0,0,1,0,0,0,1,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,1, + 0,1,1,0,0,0,1,1, + 0,0,1,1,1,1,1,0, + 0,0,0,1,1,1,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,1,0,0,1, + 0,1,0,0,1,0,0,1, + 0,1,1,0,0,0,1,1, + 0,1,1,0,0,0,1,1, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,1,0,0,1, + 0,0,0,0,1,0,0,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,0,0,0,1,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,1,0,0,1, + 0,1,1,1,1,0,1,1, + 0,1,1,1,1,0,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,0,0,0,0, + 0,1,1,1,0,0,0,0, + 0,1,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,0,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,1,0,0,0, + 0,0,0,1,1,1,0,0, + 0,1,1,1,0,1,1,1, + 0,1,1,0,0,0,1,1, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,0, + 0,1,1,0,0,0,0,0, + 0,1,1,1,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,0, + 0,0,0,0,1,1,0,0, + 0,0,0,1,1,0,0,0, + 0,0,0,0,1,1,0,0, + 0,1,1,1,1,1,1,0, + 0,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,0,1,1,0, + 0,0,0,0,1,1,0,0, + 0,0,0,1,1,0,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,0,0,0,1,1, + 0,1,0,0,0,0,0,1, + 0,1,1,0,0,0,1,1, + 0,0,1,1,1,1,1,0, + 0,0,0,1,1,1,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,1,0,0,1, + 0,0,0,0,1,0,0,1, + 0,0,0,0,1,1,1,1, + 0,0,0,0,0,1,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,0,0,0,1,1, + 0,1,0,0,0,0,0,1, + 0,1,1,0,0,0,1,1, + 1,1,1,1,1,1,1,0, + 1,0,0,1,1,1,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,1,0,0,1, + 0,0,0,1,1,0,0,1, + 0,0,1,1,1,1,1,1, + 0,1,1,0,0,1,1,0, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,0,0,1,1,0, + 0,1,1,0,1,1,1,1, + 0,1,0,0,1,1,0,1, + 0,1,0,1,1,0,0,1, + 0,1,1,1,0,0,1,1, + 0,0,1,1,0,0,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1, + 0,0,0,0,1,1,1,1, + 0,0,1,1,1,1,0,0, + 0,1,1,1,0,0,0,0, + 0,1,1,1,0,0,0,0, + 0,0,1,1,1,1,0,0, + 0,0,0,0,1,1,1,1, + 0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,1,1,0,0,0,0, + 0,0,0,1,1,0,0,0, + 0,0,1,1,0,0,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,0,0,0,1,1, + 0,0,1,1,0,1,1,0, + 0,0,0,1,1,1,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,0,1,1,0, + 0,1,1,0,0,0,1,1, + 0,1,0,0,0,0,0,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,1,1,1, + 0,1,0,0,1,1,0,0, + 0,1,1,1,1,0,0,0, + 0,1,1,1,1,0,0,0, + 0,1,0,0,1,1,0,0, + 0,0,0,0,0,1,1,1, + 0,0,0,0,0,0,1,1, + 0,1,0,0,0,1,1,1, + 0,1,1,0,0,0,1,1, + 0,1,1,1,0,0,0,1, + 0,1,0,1,1,0,0,1, + 0,1,0,0,1,1,0,1, + 0,1,1,0,0,1,1,1, + 0,1,1,1,0,0,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,1,1,0, + 0,0,0,0,1,1,0,0, + 0,0,0,1,1,0,0,0, + 0,0,1,1,0,0,0,0, + 0,1,1,0,0,0,0,0, + 1,1,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,1,0,0, + 0,0,0,0,0,1,1,0, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,1,1,0, + 0,0,0,0,1,1,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,1,1,1, + 0,0,0,0,0,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,0,0,0,0,0, + 0,1,1,0,0,1,0,0, + 0,1,0,1,0,1,0,0, + 0,1,0,1,0,1,0,0, + 0,0,1,1,1,1,0,0, + 0,1,1,1,1,0,0,0, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1, + 0,0,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,1,0,0,0, + 0,1,0,0,0,1,0,0, + 0,1,1,1,1,1,0,0, + 0,0,1,1,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,0,0,0, + 0,1,1,1,1,1,0,0, + 0,1,0,0,0,1,0,0, + 0,1,0,0,0,1,0,0, + 0,1,1,0,1,1,0,0, + 0,0,1,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,0,0,0, + 0,1,1,1,1,1,0,0, + 0,1,0,0,0,1,0,0, + 0,1,0,0,1,0,0,1, + 0,0,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,0,0,0, + 0,1,1,1,1,1,0,0, + 0,1,0,1,0,1,0,0, + 0,1,0,1,0,1,0,0, + 0,1,0,1,1,1,0,0, + 0,0,0,1,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,1,0,0,0, + 0,1,1,1,1,1,1,0, + 0,1,1,1,1,1,1,1, + 0,1,0,0,1,0,0,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,1,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 1,1,0,1,1,0,0,0, + 1,0,1,1,1,1,0,0, + 1,0,1,0,0,1,0,0, + 1,0,1,0,0,1,0,0, + 1,1,1,1,1,0,0,0, + 0,1,0,1,1,1,0,0, + 0,0,0,0,0,1,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,0,1,0,0,0, + 0,0,0,0,0,1,0,0, + 0,1,1,1,1,1,0,0, + 0,1,1,1,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,1,0,0, + 0,1,1,1,1,1,0,1, + 0,1,1,1,1,1,0,1, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 1,1,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 1,1,1,1,1,1,0,1, + 0,1,1,1,1,1,0,1, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,0,0,1,0,0,0,0, + 0,0,1,1,1,0,0,0, + 0,1,1,0,1,1,0,0, + 0,1,0,0,0,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,1, + 0,1,1,1,1,1,1,1, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,1,1,1,1,0,0, + 0,1,1,1,1,1,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,1,0,0,0,0, + 0,0,0,0,1,1,0,0, + 0,1,1,1,1,1,0,0, + 0,1,1,1,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,1,1,1,1,0,0, + 0,1,1,1,1,1,0,0, + 0,0,0,0,0,1,0,0, + 0,0,0,0,0,1,0,0, + 0,1,1,1,1,1,0,0, + 0,1,1,1,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,0,0,0, + 0,1,1,1,1,1,0,0, + 0,1,0,0,0,1,0,0, + 0,1,0,0,0,1,0,0, + 0,1,1,1,1,1,0,0, + 0,0,1,1,1,0,0,0, + 0,0,0,0,0,0,0,0, + 1,0,0,0,0,1,0,0, + 1,1,1,1,1,1,0,0, + 1,1,1,1,1,0,0,0, + 1,0,1,0,0,1,0,0, + 0,0,1,0,0,1,0,0, + 0,0,1,1,1,1,0,0, + 0,0,0,1,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,0,0,0, + 0,0,1,1,1,1,0,0, + 0,0,1,0,0,1,0,0, + 0,0,1,0,0,1,0,0, + 1,1,1,1,1,1,0,0, + 1,1,1,1,1,0,0,0, + 1,0,0,0,0,1,0,0, + 0,1,0,0,0,1,0,0, + 0,1,1,1,1,1,0,0, + 0,1,1,1,1,1,0,0, + 0,1,0,0,1,0,0,0, + 0,0,0,0,0,1,0,0, + 0,0,0,1,1,1,0,0, + 0,0,0,1,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,1,0,0,0, + 0,1,0,1,1,1,0,0, + 0,1,0,1,0,1,0,0, + 0,1,0,1,0,1,0,0, + 0,1,1,1,0,1,0,0, + 0,0,1,0,0,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,1,1,1,1,1, + 0,1,0,0,0,1,0,0, + 0,0,1,0,0,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,1,1,1,1,0,0, + 0,1,1,1,1,1,0,0, + 0,1,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0, + 0,0,1,1,1,1,0,0, + 0,1,1,1,1,1,0,0, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0, + 0,0,1,1,1,1,0,0, + 0,1,1,0,0,0,0,0, + 0,1,1,0,0,0,0,0, + 0,0,1,1,1,1,0,0, + 0,0,0,1,1,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0, + 0,1,1,1,1,1,0,0, + 0,1,1,0,0,0,0,0, + 0,0,0,1,1,0,0,0, + 0,1,1,0,0,0,0,0, + 0,1,1,1,1,1,0,0, + 0,0,0,1,1,1,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,1,0,0, + 0,1,1,0,1,1,0,0, + 0,0,1,1,1,0,0,0, + 0,0,0,1,0,0,0,0, + 0,0,1,1,1,0,0,0, + 0,1,1,0,1,1,0,0, + 0,1,0,0,0,1,0,0, + 0,0,0,0,0,0,0,0, + 1,0,0,1,1,1,0,0, + 1,0,1,1,1,1,0,0, + 1,1,1,0,0,0,0,0, + 0,1,1,0,0,0,0,0, + 0,0,1,1,1,1,0,0, + 0,0,0,1,1,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,1,1,0,0, + 0,1,1,0,0,1,0,0, + 0,1,1,1,0,1,0,0, + 0,1,0,1,1,1,0,0, + 0,1,0,0,1,1,0,0, + 0,1,1,0,0,1,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,1,1,1,1,1,0, + 0,1,1,1,0,1,1,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,1,0, + 0,1,0,0,0,0,0,1, + 0,1,0,0,0,0,0,1, + 0,1,1,1,0,1,1,1, + 0,0,1,1,1,1,1,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,0, + 0,0,0,0,0,0,0,1, + 0,0,0,0,0,0,0,1, + 0,0,0,0,0,0,1,1, + 0,0,0,0,0,0,1,0, + 0,0,0,0,0,0,1,0, + 0,0,0,0,0,0,0,1, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 +}; diff --git a/src/libvideo/video.c b/src/libvideo/video.c index c48742f..b0cf223 100644 --- a/src/libvideo/video.c +++ b/src/libvideo/video.c @@ -27,6 +27,7 @@ static void planar2chunky(uint8_t *rgb, uint8_t *bitplanes[], uint16_t palette[], int planes, int width, int y_start, int y_end) { + uint8_t red, green, blue; int x, y, p, b, i; uint8_t word[planes], chunk; uint16_t rgb4; @@ -46,9 +47,15 @@ static void planar2chunky(uint8_t *rgb, uint8_t *bitplanes[], uint16_t palette[] word[p] <<= 1; } rgb4 = palette[chunk]; - *rgb++ = (rgb4 >> 4) & 0xf0; - *rgb++ = rgb4 & 0xf0; - *rgb++ = rgb4 << 4; + red = (rgb4 >> 4) & 0xf0; + green = rgb4 & 0xf0; + blue = rgb4 << 4; + red = red | (red >> 4); + green = green | (green >> 4); + blue = blue | (blue >> 4); + *rgb++ = red; + *rgb++ = green; + *rgb++ = blue; } i++; } @@ -83,6 +90,9 @@ static void planar2chunky2(uint8_t *rgb1, uint8_t *bitplanes[], uint16_t palette red = (rgb4 >> 4) & 0xf0; green = rgb4 & 0xf0; blue = rgb4 << 4; + red = red | (red >> 4); + green = green | (green >> 4); + blue = blue | (blue >> 4); *rgb1++ = red; *rgb1++ = green; *rgb1++ = blue; diff --git a/src/mercenary/Makefile.am b/src/mercenary/Makefile.am index 621b34c..be50a95 100644 --- a/src/mercenary/Makefile.am +++ b/src/mercenary/Makefile.am @@ -21,6 +21,7 @@ mercenary2_LDADD = \ $(top_builddir)/src/libcpu/libcpu.a \ $(top_builddir)/src/libframerate/libframerate.a \ $(top_builddir)/src/libvideo/libvideo.a \ + $(top_builddir)/src/libtext/libtext.a \ $(top_builddir)/src/libsound/libsound.a \ $(top_builddir)/src/libjoystick/libjoystick.a \ $(top_builddir)/src/libdisk/libdisk.a \ @@ -39,6 +40,7 @@ mercenary3_LDADD = \ $(top_builddir)/src/libcpu/libcpu.a \ $(top_builddir)/src/libframerate/libframerate.a \ $(top_builddir)/src/libvideo/libvideo.a \ + $(top_builddir)/src/libtext/libtext.a \ $(top_builddir)/src/libsound/libsound.a \ $(top_builddir)/src/libjoystick/libjoystick.a \ $(top_builddir)/src/libdisk/libdisk.a \ diff --git a/src/mercenary/main.c b/src/mercenary/main.c index cda13e1..866d795 100644 --- a/src/mercenary/main.c +++ b/src/mercenary/main.c @@ -33,6 +33,7 @@ #include "../libjoystick/joystick.h" #include "../libkeyboard/keyboard.h" #include "../libdisk/disk.h" +#include "../libtext/text.h" #include "mercenary.h" #include "render.h" @@ -81,12 +82,19 @@ static int config_improve_extend_roads = 1; /* set to 1 to extend roads */ static uint8_t *memory = NULL; static uint8_t *stop_event = NULL; static uint8_t *image = NULL; +static uint8_t *help_osd = NULL; +static uint8_t *info_osd = NULL; +static int help_view = 1; +static int32_t osd_timer = 0; #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 200 #define IMAGE_WIDTH 320 #define IMAGE_HEIGHT 200 #define BENSON_AT_LINE 136 #define IMAGE_DIWSTART 0x2c +#define HELP_ALPHA 0xd0 +#define OSD_WIDTH 320 +#define OSD_HEIGHT 16 static uint16_t *chipreg = NULL; static stereo_t *sound_buffer = NULL; /* sound buffer memory */ static int sound_buffer_size; /* buffer sample size */ @@ -304,9 +312,12 @@ static void main_loop(void) /* amgia speed: don't render if we still delay */ /* non amiga speed: render if we need a new frame */ /* NOTE: at input event we must render after every VBL, so we do this in every loop */ - if ((frame_render && !config_amiga_speed) - || (config_amiga_speed && render_delay <= 0.0) - || event == STOP_AT_WAIT_INPUT) { + /* in case of help view: stop cpu after first IRQ, regardless of other options */ + /* NOTE: We need initial IRQ, so we have out copper list initialized */ + if (((frame_render && !config_amiga_speed) + || (config_amiga_speed && render_delay <= 0.0) + || event == STOP_AT_WAIT_INPUT) + && !(had_first_irq && help_view)) { frame_render = 0; /* start capturing for improved graphics */ if (render_improved) @@ -333,12 +344,14 @@ static void main_loop(void) /* render improved graphics, interpolate, if required */ if (render_improved) render_all_items((config_amiga_speed) ? 1.0 : frame_time); - /* advance frame time */ -printf("frame rate: %.6f, frame-step=%.5f frame-time=%.5f\n", 1.0 / vbl_duration, frame_step,frame_time); - frame_time += frame_step; - if (frame_time >= 1.0) { - frame_time -= 1.0; - frame_render = 1; + /* advance frame time, if we are not in help view */ + if (!(had_first_irq && help_view)) { +//printf("frame rate: %.6f, frame-step=%.5f frame-time=%.5f\n", 1.0 / vbl_duration, frame_step,frame_time); + frame_time += frame_step; + if (frame_time >= 1.0) { + frame_time -= 1.0; + frame_render = 1; + } } /* STEP 2: transfer legacy image (or just benson) in memory to OpenGL texture */ @@ -349,70 +362,90 @@ printf("frame rate: %.6f, frame-step=%.5f frame-time=%.5f\n", 1.0 / vbl_duration if (render_legacy) emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, 0, BENSON_AT_LINE, double_pixel_size); } - /* render benson on improved rendering, if enabled */ - if (render_improved) + /* render benson + osd ontop of improved opengl rendering, if enabled */ + if (render_improved) { opengl_blit_benson(image, config_video_filter, (double_pixel_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE, config_fov, benson_size, (double_pixel_size) ? 2 : 1); + if (help_view) + opengl_blit_osd(0, help_osd, config_video_filter, (double_pixel_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE, config_fov, benson_size, 1.0, 1.0, 0.0, 0.0); + if (osd_timer) { + opengl_blit_osd(1, info_osd, config_video_filter, (double_pixel_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE, config_fov, benson_size, 0.5, 0.04, 0.5, -0.95); + if (osd_timer - (int32_t)SDL_GetTicks() < 0) + osd_timer = 0; + } + } /* setup viewport for legacy image and render image, if enabled */ if (render_legacy) { opengl_viewport_legacy(debug_opengl); opengl_blit_legacy(image, config_video_filter); + if (help_view) + opengl_blit_osd(0, help_osd, config_video_filter, 0, 0.0, 0, 1.0, 1.0, 0.0, 0.0); + if (osd_timer) { + opengl_blit_osd(1, info_osd, config_video_filter, 0, 0.0, 0, 0.5, 0.04, 0.5, -0.95); + if (osd_timer - (int32_t)SDL_GetTicks() < 0) + osd_timer = 0; + } } - /* wait for VBL and show */ + + /* at this point we are ready with our image, so we display */ swap_sdl(); /* measure frame rate */ framerate_measure(); /* STEP 3: execute interrupt at rate of 50Hz, render sound */ - current_time = SDL_GetTicks(); - diff = current_time - last_time; - /* in case of timer glitch, execute IRQ only by maximum number of SOUND_CHUNKS */ - if (diff > 1000 * SOUND_CHUNKS / IRQ_RATE) { - diff = 1000 * SOUND_CHUNKS / IRQ_RATE; - last_time = current_time - 1000 * SOUND_CHUNKS / IRQ_RATE; - } - while (diff > 1000 / IRQ_RATE) { - execute_cpu(3, NULL); - execute_cpu(4, NULL); - had_first_irq = 1; - /* transfer benson without game view - * because we only got benson refreshed during VBL IRQ - */ - emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, BENSON_AT_LINE, IMAGE_HEIGHT, double_pixel_size); - /* render sound to sound buffer - * buffer pointer read and write is atomic, so no locking required! - */ - space = (sound_buffer_readp - sound_buffer_writep - 1 + sound_buffer_size) % sound_buffer_size; - if (space < SOUND_SAMPLERATE / IRQ_RATE) { + /* only do this, if we are not in help view */ + /* NOTE: We need initial IRQ, so we have out copper list initialized */ + if (!(had_first_irq && help_view)) { + current_time = SDL_GetTicks(); + diff = current_time - last_time; + /* in case of timer glitch, execute IRQ only by maximum number of SOUND_CHUNKS */ + if (diff > 1000 * SOUND_CHUNKS / IRQ_RATE) { + diff = 1000 * SOUND_CHUNKS / IRQ_RATE; + last_time = current_time - 1000 * SOUND_CHUNKS / IRQ_RATE; + } + while (diff > 1000 / IRQ_RATE) { + execute_cpu(3, NULL); + execute_cpu(4, NULL); + had_first_irq = 1; + /* transfer benson without game view + * because we only got benson refreshed during VBL IRQ + */ + emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, BENSON_AT_LINE, IMAGE_HEIGHT, double_pixel_size); + /* render sound to sound buffer + * buffer pointer read and write is atomic, so no locking required! + */ + space = (sound_buffer_readp - sound_buffer_writep - 1 + sound_buffer_size) % sound_buffer_size; + if (space < SOUND_SAMPLERATE / IRQ_RATE) { #ifdef DEBUG_SOUND_BUFFERING - fprintf(stderr, "sound buffer overflow\n"); + fprintf(stderr, "sound buffer overflow\n"); #endif - length = space; - } else - length = SOUND_SAMPLERATE / IRQ_RATE; + length = space; + } else + length = SOUND_SAMPLERATE / IRQ_RATE; #ifdef DEBUG_SOUND_BUFFERING - printf("can write %d, write %d\n", space, length); - static int cnt = 0; - int s; - for (s = 0; s < length; s++) { - sound_buffer[(sound_buffer_writep + s) % sound_buffer_size].left = - sound_buffer[(sound_buffer_writep + s) % sound_buffer_size].right - = sin(2 * M_PI * 999 * cnt / SOUND_SAMPLERATE) - * sin(2 * M_PI * 21 * cnt / SOUND_SAMPLERATE) - * sin(2 * M_PI * 0.5 * cnt / SOUND_SAMPLERATE); - cnt++; - } + printf("can write %d, write %d\n", space, length); + static int cnt = 0; + int s; + for (s = 0; s < length; s++) { + sound_buffer[(sound_buffer_writep + s) % sound_buffer_size].left = + sound_buffer[(sound_buffer_writep + s) % sound_buffer_size].right + = sin(2 * M_PI * 999 * cnt / SOUND_SAMPLERATE) + * sin(2 * M_PI * 21 * cnt / SOUND_SAMPLERATE) + * sin(2 * M_PI * 0.5 * cnt / SOUND_SAMPLERATE); + cnt++; + } #else - render_sound(memory, sound_buffer, sound_buffer_size, sound_buffer_writep, length, config_audio_filter); + render_sound(memory, sound_buffer, sound_buffer_size, sound_buffer_writep, length, config_audio_filter); #endif - sound_buffer_writep = (sound_buffer_writep + length) % sound_buffer_size; - diff -= 1000 / IRQ_RATE; - last_time += 1000 / IRQ_RATE; - - /* count down render delay */ - if (render_delay) { - render_delay -= 1.0 / (double)IRQ_RATE; - if (render_delay < 0.0) - render_delay = 0.0; + sound_buffer_writep = (sound_buffer_writep + length) % sound_buffer_size; + diff -= 1000 / IRQ_RATE; + last_time += 1000 / IRQ_RATE; + + /* count down render delay */ + if (render_delay) { + render_delay -= 1.0 / (double)IRQ_RATE; + if (render_delay < 0.0) + render_delay = 0.0; + } } } } @@ -540,6 +573,23 @@ fail: } } +static void osd_info(const char *param, const char *value) +{ + char line[41] = " "; + + if (param[0]) { + strncpy(line + 21 - strlen(param), param, strlen(param)); + line[22] = ':'; + } + if (strlen(value) > 15) { + print_error("string too long\n"); + return; + } + strncpy(line + 25, value, strlen(value)); + text_render(info_osd, OSD_WIDTH, OSD_HEIGHT, line, 0x00, 4, 0, 0, 1); + osd_timer = SDL_GetTicks() + 2500; +} + static int shift = 0, ctrl = 0; static void keyboard_sdl(int down, SDL_Keycode sym) @@ -553,23 +603,30 @@ static void keyboard_sdl(int down, SDL_Keycode sym) if (ctrl && down) { switch (sym) { + case SDLK_h: + help_view ^= 1; + break; case SDLK_v: config_video_filter ^= 1; - print_info("video filter: %s\n", (config_video_filter) ? "on" : "off"); + osd_info("video filter", (config_video_filter) ? "on" : "off"); break; case SDLK_a: config_audio_filter ^= 1; - print_info("audio filter: %s\n", (config_audio_filter) ? "on" : "off"); + osd_info("audio filter", (config_audio_filter) ? "on" : "off"); break; case SDLK_s: config_amiga_speed ^= 1; - print_info("amiga speed: %s\n", (config_amiga_speed) ? "original" : "full"); + osd_info("render speed", (config_amiga_speed) ? "original" : "fast"); break; case SDLK_r: config_render ^= 1; - print_info("render: %s\n", (config_render) ? "opengl" : "original"); + osd_info("render mode", (config_render) ? "OpenGL" : "original"); break; case SDLK_b: + if (!config_render) { + osd_info("", "not applicable"); + break; + } if (config_benson_size == 0.5) { config_benson_size = 1.0; config_fov = FOV_NOVAGEN; @@ -577,7 +634,7 @@ static void keyboard_sdl(int down, SDL_Keycode sym) config_benson_size = 0.5; config_fov = FOV_JOLLY; } - print_info("benson: %s\n", (config_benson_size == 0.5) ? "half" : "normal"); + osd_info("Benson size", (config_benson_size == 0.5) ? "half" : "normal"); break; case SDLK_c: if (config_ctrl_c) @@ -586,18 +643,29 @@ static void keyboard_sdl(int down, SDL_Keycode sym) case SDLK_KP_PLUS: if (config_fov / 1.2 >= FOV_MIN) config_fov /= 1.2; - print_info("FOV: %.2f\n", config_fov); + disp_fov: + { + char text[16]; + sprintf(text, "%.2f", config_fov); + osd_info("FOV", text); + } break; case SDLK_KP_MINUS: if (config_fov * 1.2 <= FOV_MAX) config_fov *= 1.2; - print_info("FOV: %.2f\n", config_fov); - break; + goto disp_fov; } /* do not pass keys to game while holding CTRL */ return; } + if (sym == SDLK_PAUSE && down) + help_view ^= 1; + + /* in help view we don't need to forward keypresses */ + if (help_view) + return; + switch (sym) { case SDLK_LSHIFT: set_key("LSH", down); @@ -833,6 +901,63 @@ int main(int argc, char *argv[]) goto done; resize_opengl((config_debug_opengl) ? SCREEN_WIDTH * 2 : SCREEN_WIDTH * 3, (config_debug_opengl) ? SCREEN_HEIGHT * 4 : SCREEN_HEIGHT * 3); + /* init osd */ + rc = init_osd(0, IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2); + if (rc < 0) + goto done; + rc = init_osd(1, OSD_WIDTH, OSD_HEIGHT); + if (rc < 0) + goto done; + help_osd = text_alloc(IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2, HELP_ALPHA); + if (!help_osd) + goto done; + text_render(help_osd, IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2, mercenary_name, HELP_ALPHA, 3, (double)(80 - strlen(mercenary_name)) / 2.0, 1, 1); + text_render(help_osd, IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2, + "Emulation:\n" + " Press `PAUSE' to toggle this help screen on or off.\n" + " Press `CTRL' + `F' to toggle between full screen / window mode.\n" + " Press `CTRL' + `R' to toggle between original / OpenGL rendering.\n" + " Press `CTRL' + `S' to toggle between original / fast rendering.\n" + " Press `CTRL' + `B' to toggle between large / small Benson.\n" + " Press `CTRL' + `V' to toggle video filter on / off.\n" + " Press `CTRL' + `A' to toggle audio filter on / off.\n" + " Press `CTRL' + `+' or `-' to change field-of-view (OpenGL).\n" + " Press `CTRL' + `I' to skip intro (approaching to Eris).\n" + "\n" + "Answer to a Question:\n" + " Press `O' (not Zero) for OK and other key for NO.\n" + "\n" + "Walking / Driving / Flying:\n" + " Use cursor keys as joystick, to move player / craft.\n" + " Press `R' for running, 'W' for walking speed.\n" + " Press `B' to board, `L' to leave.\n" + " Press `1'...`0' to drive / fly (slow...fast), `+' or `-' to adjust.\n" + " Press `F1'...`F0' to drive / fly backwards (slow...fast).\n" + " Press `SPACE' to stop craft.\n" + " Press `ESCAPE' to activate escape sequence on crafts.\n" + " Press `T' for turbo on certain craft.\n" + " Press `END' as joystick's fire button.\n" + "\n" + "Elevator:\n" + " Press `1'...`9' to select floor, 'G' for ground, `B' for basement.\n" + "\n" + "Inside a transporter:\n" + " Press `1'...`0' to select destination.\n" + "\n" + "Items:\n" + " Press `SHFIT' + cursor left / right keys to choose item.\n" + " Press `SHFIT' + cursor up / down keys to pickup / drop item.\n" + " Press `NUMPAD Enter' to select certain items.\n" + " Press `NUMPAD +' / `NUMPAD -' to select entries.\n" + " Press `NUMPAD *' to read entry\n" + "Saving / Loading / Pause:\n" + " Press `INSERT' to loading and saving options.\n" + " Press `ENTER' to pause game, other key to continue.\n" + ,HELP_ALPHA, 1, 2, 5, 0); + info_osd = text_alloc(OSD_WIDTH, OSD_HEIGHT, 0x00); + if (!info_osd) + goto done; + /* init audio filter */ sound_init_filter(SOUND_SAMPLERATE); @@ -851,20 +976,6 @@ int main(int argc, char *argv[]) } while (event != STOP_AT_WAIT_VBL); } - print_info("**********************************\n"); - print_info("* Welcome to Mercenary Reloaded! *\n"); - print_info("**********************************\n\n"); - print_info("Press CTRL + cursor keys to select inventory or pickup/drop item.\n"); - print_info("Press CTRL + f to toggle full screen.\n"); - print_info("Press CTRL + s to toggle rendering speed.\n"); - print_info("Press CTRL + v to toggle video filter.\n"); - print_info("Press CTRL + a to toggle audio filter.\n"); - print_info("Press CTRL + r to toggle rendering with opengl or orignal code.\n"); - print_info("Press CTRL + Keypad Plus to decrement FOV.\n"); - print_info("Press CTRL + Keypad Minus to increment FOV.\n"); - print_info("Press CTRL + c to exit game.\n\n"); - print_info("Use '--help' as command line option for configuration settings.\n\n"); - /* run game */ main_loop(); @@ -882,6 +993,10 @@ done: free(sound_buffer); if (image) free(image); + if (help_osd) + free(help_osd); + if (info_osd) + free(info_osd); return 0; } diff --git a/src/mercenary/mercenary.h b/src/mercenary/mercenary.h index 772064a..c3c2a92 100644 --- a/src/mercenary/mercenary.h +++ b/src/mercenary/mercenary.h @@ -83,5 +83,6 @@ uint16_t mercenary_poly_tags_color(void); int mercenary_background_index(void); uint32_t mercenary_planet_scale_index(void); uint32_t mercenary_star_table(void); +extern const char *mercenary_name; extern const char *mercenary_gamesavesuffix; diff --git a/src/mercenary/mercenary2.c b/src/mercenary/mercenary2.c index 1267c75..47b8af5 100644 --- a/src/mercenary/mercenary2.c +++ b/src/mercenary/mercenary2.c @@ -262,5 +262,6 @@ uint32_t mercenary_star_table(void) return 0x005D8C0; } +const char *mercenary_name = "Mercenary II - Damocles"; const char *mercenary_gamesavesuffix = ".m2save"; diff --git a/src/mercenary/mercenary3.c b/src/mercenary/mercenary3.c index c2ca7f1..28751e2 100644 --- a/src/mercenary/mercenary3.c +++ b/src/mercenary/mercenary3.c @@ -300,5 +300,6 @@ uint32_t mercenary_star_table(void) return DS_84+0x6A; } +const char *mercenary_name = "Mercenary III - The Dion Crisis"; const char *mercenary_gamesavesuffix = ".m3save"; diff --git a/src/mercenary/render.c b/src/mercenary/render.c index 9fbf45f..85498fc 100644 --- a/src/mercenary/render.c +++ b/src/mercenary/render.c @@ -335,6 +335,13 @@ void render_capture_start(double _fov, int _extend_roads, int debug) void render_capture_stop(void) { + /* no new list (due to STOP_AT_WAIT_INPUT), use old list, if any */ + if (!render_list_new) { + render_list_new = render_list_old; + render_list_end = &render_list_new; + render_list_old = NULL; + } + } static void gamecolor2gl_index(double *red, double *green, double *blue, uint16_t index) @@ -2522,6 +2529,18 @@ void render_all_items(double inter) interpolate_objects(inter); } + /* add a blank background, if render list is empty */ + if (!render_list_new) { + render_item_t blank; + memset(&blank, 0, sizeof(blank)); + blank.type = RENDER_ITEM_SKY; + blank.u.sky.red = 0.5; + blank.u.sky.green = 0.5; + blank.u.sky.blue = 0.5; + render_one_item(&blank); + return; + } + for (render_item = render_list_new; render_item; render_item = render_item->next) { render_one_item(render_item); }