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
src/libkeyboard/Makefile
src/libdisk/Makefile
src/libsdl/Makefile
+ src/libtext/Makefile
src/mercenary/Makefile
src/Makefile
Makefile)
libjoystick \
libkeyboard \
libdisk \
- libsound
+ libsound \
+ libtext
if HAVE_SDL2
if HAVE_GLEW
#include "opengl.h"
#include <GL/glew.h>
+#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)
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)
{
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);
/* 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);
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)
{
/* free image texture */
void exit_opengl(void)
{
+ int i;
+
if (legacy_rgb) {
free(legacy_rgb);
legacy_rgb = NULL;
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;
+ }
+ }
}
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);
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);
--- /dev/null
+AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
+
+noinst_LIBRARIES = libtext.a
+
+libtext_a_SOURCES = \
+ text.c
+
--- /dev/null
+/* Text screens and OSD generator
+ *
+ * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#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++;
+ }
+}
+
--- /dev/null
+
+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);
+
--- /dev/null
+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
+};
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;
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++;
}
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;
$(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 \
$(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 \
#include "../libjoystick/joystick.h"
#include "../libkeyboard/keyboard.h"
#include "../libdisk/disk.h"
+#include "../libtext/text.h"
#include "mercenary.h"
#include "render.h"
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 */
/* 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)
/* 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 */
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;
+ }
}
}
}
}
}
+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)
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;
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)
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);
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);
} 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();
free(sound_buffer);
if (image)
free(image);
+ if (help_osd)
+ free(help_osd);
+ if (info_osd)
+ free(info_osd);
return 0;
}
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;
return 0x005D8C0;
}
+const char *mercenary_name = "Mercenary II - Damocles";
const char *mercenary_gamesavesuffix = ".m2save";
return DS_84+0x6A;
}
+const char *mercenary_name = "Mercenary III - The Dion Crisis";
const char *mercenary_gamesavesuffix = ".m3save";
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)
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);
}