mirror of
https://github.com/libretro/libretro-samples.git
synced 2025-04-02 10:31:48 -04:00
787 lines
19 KiB
C
787 lines
19 KiB
C
/*
|
|
|
|
Copyright (c) 2015-2016, Higor Eurípedes
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
|
list of conditions and the following disclaimer in the documentation and/or
|
|
other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "libretro.h"
|
|
#include "ttf2c.h"
|
|
#include "font24.h"
|
|
#include "font16.h"
|
|
#include "font10.h"
|
|
|
|
static retro_environment_t env_cb = NULL;
|
|
static retro_input_state_t input_cb = NULL;
|
|
static retro_input_poll_t poll_cb = NULL;
|
|
static retro_video_refresh_t video_cb = NULL;
|
|
|
|
static bool bool_env(unsigned env, bool value) { return env_cb(env, &value); }
|
|
static bool uint_env(unsigned env, unsigned value) { return env_cb(env, &value); }
|
|
|
|
|
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
|
|
|
#define SCREEN_WIDTH 640
|
|
#define SCREEN_HEIGHT 360
|
|
#define SCREEN_MAX_POS MAX(SCREEN_WIDTH, SCREEN_HEIGHT)
|
|
#define SCREEN_PITCH (SCREEN_MAX_POS * sizeof(uint16_t))
|
|
#define CELL_SIZE 28
|
|
#define GRID_GROUP_SIZE 3
|
|
#define GRID_COLS (GRID_GROUP_SIZE*6)
|
|
#define GRID_ROWS (GRID_GROUP_SIZE*3)
|
|
#define GRID_X 100
|
|
#define GRID_Y 70
|
|
|
|
static inline uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b)
|
|
{
|
|
return ((r >> 3U) << 11U)
|
|
| ((g >> 2U) << 5U)
|
|
| ((b >> 3U) << 0U);
|
|
}
|
|
|
|
static inline void rgb565_decompose(uint16_t color, uint8_t *r, uint8_t *g, uint8_t *b)
|
|
{
|
|
*r = ((color >> 11) << 3) & 0xff;
|
|
*g = ((color >> 5) << 2) & 0xff;
|
|
*b = ((color >> 0) << 3) & 0xff;
|
|
}
|
|
|
|
#define WHITE rgb565(255, 255, 255)
|
|
#define BLACK rgb565(255, 255, 255)
|
|
#define GREY rgb565(63, 63, 63)
|
|
#define LIGHTGREY RGB(127, 127, 127)
|
|
|
|
enum
|
|
{
|
|
XALIGN_LEFT = 0,
|
|
XORIG_RIGHT = 1 << 0,
|
|
XORIG_CENTER = 1 << 1,
|
|
YALIGN_TOP = 0,
|
|
YORIG_BOTTOM = 1 << 2,
|
|
YORIG_CENTER = 1 << 3
|
|
};
|
|
|
|
typedef enum
|
|
{
|
|
CELL_CLEAR = 0,
|
|
CELL_SET = 1,
|
|
CELL_DISABLED = 2,
|
|
CELL_WRONG = 3,
|
|
} cell_state_t;
|
|
|
|
typedef cell_state_t grid_t[GRID_ROWS][GRID_COLS];
|
|
|
|
struct {
|
|
struct
|
|
{
|
|
grid_t front; // modifiable by the user
|
|
grid_t back; // the grid to test against
|
|
int focus_x, focus_y;
|
|
} grid;
|
|
|
|
int row_counts[GRID_ROWS][GRID_COLS];
|
|
int col_counts[GRID_COLS][GRID_ROWS];
|
|
|
|
bool first_run;
|
|
|
|
uint16_t fb[SCREEN_MAX_POS * SCREEN_MAX_POS];
|
|
|
|
|
|
struct {
|
|
int x, y;
|
|
bool left, right;
|
|
bool prev_left, prev_right;
|
|
bool moved;
|
|
} mouse;
|
|
|
|
struct {
|
|
int x, y;
|
|
bool a, b;
|
|
bool moved;
|
|
} joy;
|
|
|
|
struct
|
|
{
|
|
unsigned active;
|
|
unsigned hot;
|
|
unsigned last_hot;
|
|
|
|
unsigned last_focus;
|
|
unsigned focus;
|
|
} ui;
|
|
} g;
|
|
|
|
static int16_t read_mouse(unsigned id)
|
|
{
|
|
return input_cb(0, RETRO_DEVICE_MOUSE, 0, id);
|
|
}
|
|
|
|
static int16_t read_joypad(unsigned id)
|
|
{
|
|
return input_cb(0, RETRO_DEVICE_JOYPAD, 0, id);
|
|
}
|
|
|
|
static inline void clip_pos(int *x, int *y)
|
|
{
|
|
if (x)
|
|
*x = MAX(MIN(*x, SCREEN_WIDTH), 0);
|
|
|
|
if (y)
|
|
*y = MAX(MIN(*y, SCREEN_HEIGHT), 0);
|
|
}
|
|
|
|
static inline void *memset16(void *dst, uint16_t value, size_t count)
|
|
{
|
|
if (count > 0)
|
|
{
|
|
uint16_t *dst16 = (uint16_t*)dst;
|
|
int n = (count + 7) >> 3;
|
|
|
|
switch (count & 7)
|
|
{
|
|
case 0: do { *dst16++ = value;
|
|
case 7: *dst16++ = value;
|
|
case 6: *dst16++ = value;
|
|
case 5: *dst16++ = value;
|
|
case 4: *dst16++ = value;
|
|
case 3: *dst16++ = value;
|
|
case 2: *dst16++ = value;
|
|
case 1: *dst16++ = value; } while (--n);
|
|
}
|
|
|
|
return dst16;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
static void draw_rect(int x1, int y1, int x2, int y2, uint16_t color)
|
|
{
|
|
uint16_t *pos = NULL;
|
|
int height, width;
|
|
|
|
clip_pos(&x1, &y1);
|
|
clip_pos(&x2, &y2);
|
|
|
|
if (x2 - x1 <= 0 || y2 - y1 <= 0)
|
|
return;
|
|
|
|
height = y2 - y1;
|
|
width = x2 - x1;
|
|
|
|
pos = &g.fb[y1 * SCREEN_MAX_POS + x1];
|
|
|
|
/* use system's memset for black and white */
|
|
if (color == 0xffff || color == 0)
|
|
{
|
|
width <<= 1;
|
|
color >>= 8;
|
|
|
|
do
|
|
{
|
|
memset(pos, color, width);
|
|
|
|
pos += SCREEN_MAX_POS;
|
|
} while (--height);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
memset16(pos, color, width);
|
|
|
|
pos += SCREEN_MAX_POS;
|
|
} while (--height);
|
|
}
|
|
}
|
|
|
|
static void draw_square(int x, int y, int side, uint16_t color)
|
|
{
|
|
draw_rect(x, y, x+side, y+side, color);
|
|
}
|
|
|
|
void draw_str(const ttf2c_font *font, int x1, int y1, unsigned orig, uint16_t color, const char *str)
|
|
{
|
|
uint8_t sr, sg, sb;
|
|
|
|
int line_width = ttf2c_get_width(font, str);
|
|
int line_height = font->line_height;
|
|
|
|
if (orig & XORIG_RIGHT)
|
|
x1 -= line_width;
|
|
else if (orig & XORIG_CENTER)
|
|
x1 -= line_width / 2;
|
|
|
|
if (orig & YORIG_BOTTOM)
|
|
y1 -= line_height;
|
|
else if (orig & YORIG_CENTER)
|
|
y1 -= line_height / 2;
|
|
|
|
int x2 = x1 + ttf2c_get_width(font, str);
|
|
int y2 = y1 + line_height;
|
|
|
|
clip_pos(&x1, &y1);
|
|
clip_pos(&x2, &y2);
|
|
|
|
if (x2 - x1 <= 0 || y2 - y1 <= 0 || !*str)
|
|
return;
|
|
rgb565_decompose(color, &sr, &sg, &sb);
|
|
|
|
do {
|
|
const ttf2c_glyph *glyph = &font->glyphs[(unsigned)*str++];
|
|
|
|
if (glyph->width * glyph->height && glyph->bitmap)
|
|
{
|
|
int x, y;
|
|
int target_y = y1 + font->ascent + glyph->off_y - font->descent;
|
|
int height = MIN(glyph->height, (y2 - y1));
|
|
int width = glyph->width;
|
|
|
|
for (y = 0; y < height; ++y)
|
|
{
|
|
const uint8_t *src = &glyph->bitmap[y * width];
|
|
uint16_t *dst = &g.fb[target_y * SCREEN_MAX_POS + x1];
|
|
for (x = 0; x < width; ++x)
|
|
{
|
|
uint8_t dr, dg, db;
|
|
rgb565_decompose(*dst, &dr, &dg, &db);
|
|
*dst = rgb565(
|
|
sr * *src / 255 + dr * (255 - *src) / 255,
|
|
sg * *src / 255 + dg * (255 - *src) / 255,
|
|
sb * *src / 255 + db * (255 - *src) / 255);
|
|
++dst;
|
|
++src;
|
|
}
|
|
target_y++;
|
|
}
|
|
}
|
|
|
|
x1 += glyph->adv_x;
|
|
|
|
if (glyph->adv_x <= glyph->width)
|
|
x1++;
|
|
|
|
} while (*str);
|
|
}
|
|
|
|
/* UI CODE ---------------------------------------------------------------- */
|
|
static void ui_begin(void)
|
|
{
|
|
g.ui.hot = 0;
|
|
}
|
|
|
|
static void ui_end(void)
|
|
{
|
|
if (!g.mouse.left && !g.mouse.right)
|
|
g.ui.active = 0;
|
|
else if (g.ui.active == 0)
|
|
g.ui.active = -1;
|
|
}
|
|
|
|
static bool ui_mouse_over(int x1, int y1, int x2, int y2)
|
|
{
|
|
unsigned mx = g.mouse.x;
|
|
unsigned my = g.mouse.y;
|
|
|
|
return mx >= x1 && mx <= x2 && my >= y1 && my <= y2;
|
|
}
|
|
|
|
static bool ui_mouse_down(bool left, bool right)
|
|
{
|
|
return (left && g.mouse.left)
|
|
|| (right && g.mouse.right);
|
|
}
|
|
|
|
static bool ui_mouse_up(bool left, bool right)
|
|
{
|
|
return (left && !g.mouse.left && g.mouse.prev_left)
|
|
|| (right && !g.mouse.right && g.mouse.prev_right);
|
|
}
|
|
|
|
static void ui_check_rect_hotness(unsigned id, int x1, int y1, int x2, int y2)
|
|
{
|
|
bool inside = ui_mouse_over(x1, y1, x2, y2);
|
|
|
|
if (inside)
|
|
{
|
|
g.ui.last_hot = g.ui.hot;
|
|
g.ui.hot = id;
|
|
}
|
|
}
|
|
|
|
static void ui_check_focus(unsigned id)
|
|
{
|
|
if (g.ui.focus == 0)
|
|
g.ui.focus = id;
|
|
}
|
|
|
|
static bool ui_button_down(unsigned id, bool left, bool right, int x1, int y1, int x2, int y2)
|
|
{
|
|
bool result = false;
|
|
ui_check_rect_hotness(id, x1, y1, x2, y2);
|
|
|
|
if (g.ui.hot == id && ui_mouse_down(left, right))
|
|
{
|
|
g.ui.last_focus = g.ui.focus;
|
|
g.ui.focus = id;
|
|
g.ui.active = id;
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool ui_button_up(unsigned id, bool left, bool right, int x1, int y1, int x2, int y2)
|
|
{
|
|
bool result = false;
|
|
ui_check_rect_hotness(id, x1, y1, x2, y2);
|
|
|
|
if (g.ui.active == id && ui_mouse_up(left, right))
|
|
{
|
|
if (g.ui.hot == id)
|
|
result = true;
|
|
|
|
g.ui.active = 0;
|
|
}
|
|
|
|
ui_button_down(id, left, right, x1, y1, x2, y2);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int ui_joy_ab(unsigned id)
|
|
{
|
|
int result = 0;
|
|
|
|
if (!g.ui.focus)
|
|
g.ui.focus = id;
|
|
|
|
if (g.ui.focus == id)
|
|
result = g.joy.a - g.joy.b;
|
|
|
|
return result;
|
|
}
|
|
|
|
/* GRID CODE ---------------------------------------------------------------- */
|
|
|
|
static bool grid_valid_cell(int row, int col)
|
|
{
|
|
return row >= 0 && row < GRID_ROWS && col >= 0 && col < GRID_COLS;
|
|
}
|
|
|
|
static void grid_clip(int *row, int *col)
|
|
{
|
|
if (row)
|
|
*row = (*row < 0) ? 0 : ((*row >= GRID_ROWS) ? GRID_ROWS - 1 : *row);
|
|
if (col)
|
|
*col = (*col < 0) ? 0 : ((*col >= GRID_COLS) ? GRID_COLS - 1 : *col);
|
|
}
|
|
|
|
static void grid_to_screen(int row, int col, int *x, int *y)
|
|
{
|
|
if (!grid_valid_cell(row, col))
|
|
{
|
|
if (x)
|
|
*x = INT_MIN;
|
|
if (y)
|
|
*y = INT_MIN;
|
|
}
|
|
if (x)
|
|
*x = GRID_X + col * CELL_SIZE + col + (col / GRID_GROUP_SIZE);
|
|
if (y)
|
|
*y = GRID_Y + row * CELL_SIZE + row + (row / GRID_GROUP_SIZE);
|
|
}
|
|
|
|
static void grid_from_screen(int x, int y, int *row, int *col)
|
|
{
|
|
/* many thanks to heez for helping me with the math */
|
|
if (row)
|
|
{
|
|
*row = (GRID_GROUP_SIZE * (y - GRID_Y)) / (GRID_GROUP_SIZE * CELL_SIZE + 4);
|
|
if (*row < 0 || *row >= GRID_COLS)
|
|
*row = INT_MIN;
|
|
}
|
|
|
|
if (col)
|
|
{
|
|
*col = (GRID_GROUP_SIZE * (x - GRID_X)) / (GRID_GROUP_SIZE * CELL_SIZE + 4);
|
|
|
|
if (*col < 0 || *col >= GRID_COLS)
|
|
*col = INT_MIN;
|
|
}
|
|
}
|
|
|
|
static uint16_t grid_cell_color(grid_t grid, int row, int col)
|
|
{
|
|
if (!grid_valid_cell(row, col))
|
|
return rgb565(255, 0, 0);
|
|
|
|
cell_state_t val = grid[row][col];
|
|
switch (val)
|
|
{
|
|
case CELL_CLEAR:
|
|
case CELL_SET:
|
|
return !val * ~0;
|
|
case CELL_DISABLED:
|
|
return rgb565(127, 127, 127);
|
|
case CELL_WRONG:
|
|
return rgb565(255, 0, 0);
|
|
default:
|
|
return rgb565(0, 255, 0);
|
|
}
|
|
}
|
|
|
|
static bool ui_grid_cell(grid_t grid, int row, int col, bool *left_down)
|
|
{
|
|
unsigned id = (uintptr_t)&grid[row][col];
|
|
bool result;
|
|
int joy = 0;
|
|
int x, y;
|
|
|
|
grid_to_screen(row, col, &x, &y);
|
|
|
|
result = ui_button_up(id, true, true, x, y, x+CELL_SIZE, y+CELL_SIZE);
|
|
joy = ui_joy_ab(id);
|
|
|
|
if (g.ui.hot == id || g.ui.focus == id)
|
|
draw_square(x-1, y-1, CELL_SIZE+2, rgb565(255, 0, 0));
|
|
else if (g.ui.last_hot == id || g.ui.last_focus == id)
|
|
draw_square(x-1, y-1, CELL_SIZE+2, rgb565(0, 255, 0));
|
|
|
|
if (result)
|
|
{
|
|
*left_down = g.mouse.prev_left;
|
|
g.grid.focus_x = col;
|
|
g.grid.focus_y = row;
|
|
}
|
|
else if (joy)
|
|
{
|
|
*left_down = joy > 0;
|
|
result = true;
|
|
}
|
|
|
|
draw_square(x, y, CELL_SIZE, grid_cell_color(grid, row, col));
|
|
|
|
return result;
|
|
}
|
|
|
|
static void render_labels(void)
|
|
{
|
|
draw_rect(0, 0, GRID_X, GRID_Y, ~0);
|
|
draw_str(&font24, 10, 0, 0, rgb565(99, 0, 0), "CRUZES");
|
|
draw_str(&font24, 10, GRID_Y - font24.line_height, 0, rgb565(99, 0, 0), "00:23:04");
|
|
|
|
for (int col = 0; col < GRID_COLS; ++col)
|
|
{
|
|
int x, y;
|
|
grid_to_screen(0, col, &x, &y);
|
|
y -= 2;
|
|
|
|
for (int index = GRID_ROWS-1; index >= 0; --index)
|
|
{
|
|
char tmp[10];
|
|
|
|
if (!g.col_counts[col][index])
|
|
continue;
|
|
|
|
snprintf(tmp, sizeof(tmp), "%i", g.col_counts[col][index]);
|
|
draw_str(&font10, x + CELL_SIZE/2, y, YORIG_BOTTOM | XORIG_CENTER, ~0, tmp);
|
|
y -= 12;
|
|
}
|
|
}
|
|
|
|
for (int row = 0; row < GRID_ROWS; ++row)
|
|
{
|
|
int x, y;
|
|
grid_to_screen(row, 0, &x, &y);
|
|
x -= 4;
|
|
|
|
for (int index = GRID_COLS-1; index >= 0; --index)
|
|
{
|
|
char tmp[10];
|
|
|
|
if (!g.row_counts[row][index])
|
|
continue;
|
|
|
|
|
|
snprintf(tmp, sizeof(tmp), "%i", g.row_counts[row][index]);
|
|
draw_str(&font10, x, y + CELL_SIZE/2, YORIG_CENTER | XORIG_RIGHT, ~0, tmp);
|
|
|
|
x -= ttf2c_get_width(&font10, tmp) + 6;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void game_step()
|
|
{
|
|
draw_rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GREY);
|
|
|
|
if (g.joy.moved && !g.mouse.moved)
|
|
{
|
|
g.grid.focus_x += g.joy.x;
|
|
g.grid.focus_y += g.joy.y;
|
|
grid_clip(&g.grid.focus_y, &g.grid.focus_x);
|
|
|
|
g.ui.last_focus = g.ui.focus;
|
|
g.ui.focus = (uintptr_t)&g.grid.front[g.grid.focus_y][g.grid.focus_x];
|
|
}
|
|
|
|
for (int row = 0; row < GRID_ROWS; ++row)
|
|
{
|
|
for (int col = 0; col < GRID_COLS; ++col)
|
|
{
|
|
bool left_down = false;
|
|
if (ui_grid_cell(g.grid.front, row, col, &left_down))
|
|
{
|
|
cell_state_t *cell = &g.grid.front[row][col];
|
|
|
|
if (left_down)
|
|
{
|
|
if (*cell == CELL_CLEAR)
|
|
{
|
|
*cell = CELL_SET;
|
|
|
|
if (g.grid.back[row][col] != CELL_SET)
|
|
{
|
|
*cell = CELL_WRONG;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
switch (*cell)
|
|
{
|
|
case CELL_CLEAR: *cell = CELL_DISABLED; break;
|
|
case CELL_SET:
|
|
case CELL_DISABLED: *cell = CELL_CLEAR; break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
render_labels();
|
|
|
|
draw_str(&font16, SCREEN_WIDTH/2, SCREEN_HEIGHT-26, XORIG_CENTER, ~0, "Press START or click here for menu.");
|
|
|
|
static uint8_t blinker = 0;
|
|
draw_square(g.mouse.x, g.mouse.y, 4, rgb565(blinker, blinker, blinker));
|
|
blinker += 20;
|
|
}
|
|
|
|
|
|
RETRO_API void retro_run(void)
|
|
{
|
|
if (g.first_run)
|
|
{
|
|
g.first_run = false;
|
|
}
|
|
|
|
poll_cb();
|
|
|
|
int delta_x, delta_y;
|
|
|
|
delta_x = read_mouse(RETRO_DEVICE_ID_MOUSE_X);
|
|
delta_y = read_mouse(RETRO_DEVICE_ID_MOUSE_Y);
|
|
|
|
g.mouse.moved = delta_x || delta_y;
|
|
g.mouse.x += g.mouse.moved ? delta_x : 0;
|
|
g.mouse.y += g.mouse.moved ? delta_y : 0;
|
|
|
|
g.mouse.left = read_mouse(RETRO_DEVICE_ID_MOUSE_LEFT);
|
|
g.mouse.right = read_mouse(RETRO_DEVICE_ID_MOUSE_RIGHT);
|
|
|
|
delta_x = read_joypad(RETRO_DEVICE_ID_JOYPAD_RIGHT) - read_joypad(RETRO_DEVICE_ID_JOYPAD_LEFT);
|
|
delta_y = read_joypad(RETRO_DEVICE_ID_JOYPAD_DOWN) - read_joypad(RETRO_DEVICE_ID_JOYPAD_UP);
|
|
|
|
g.joy.moved = (delta_x != g.joy.x) || (delta_y != g.joy.y);
|
|
|
|
if (g.joy.moved)
|
|
{
|
|
g.joy.x = delta_x;
|
|
g.joy.y = delta_y;
|
|
}
|
|
|
|
g.joy.a = read_joypad(RETRO_DEVICE_ID_JOYPAD_A)
|
|
|| read_joypad(RETRO_DEVICE_ID_JOYPAD_Y);
|
|
|
|
g.joy.b = read_joypad(RETRO_DEVICE_ID_JOYPAD_B)
|
|
|| read_joypad(RETRO_DEVICE_ID_JOYPAD_X);
|
|
|
|
clip_pos(&g.mouse.x, &g.mouse.y);
|
|
|
|
ui_begin();
|
|
|
|
game_step();
|
|
|
|
ui_end();
|
|
|
|
g.mouse.prev_left = g.mouse.left;
|
|
g.mouse.prev_right = g.mouse.right;
|
|
|
|
video_cb(g.fb, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_PITCH);
|
|
}
|
|
|
|
static void load_challenge(const char *chal)
|
|
{
|
|
if (strlen(chal) != GRID_COLS * GRID_ROWS)
|
|
abort();
|
|
|
|
memset(g.row_counts, 0, sizeof(g.row_counts));
|
|
memset(g.col_counts, 0, sizeof(g.col_counts));
|
|
|
|
for (int row = 0; row < GRID_ROWS; ++row)
|
|
for (int col = 0; col < GRID_COLS; ++col)
|
|
g.grid.back[row][col] = chal[row*GRID_COLS+col] != ' ' ? CELL_SET : CELL_CLEAR;
|
|
|
|
|
|
for (int row = 0; row < GRID_ROWS; ++row)
|
|
{
|
|
bool was_set = false;
|
|
int index = 0;
|
|
for (int col = 0; col < GRID_COLS; ++col)
|
|
{
|
|
bool set = g.grid.back[row][col] == CELL_SET;
|
|
|
|
if (set != was_set)
|
|
{
|
|
was_set = set;
|
|
++index;
|
|
}
|
|
|
|
if (set)
|
|
++g.row_counts[row][index];
|
|
}
|
|
}
|
|
|
|
for (int col = 0; col < GRID_COLS; ++col)
|
|
{
|
|
bool was_set = false;
|
|
int index = 0;
|
|
for (int row = 0; row < GRID_ROWS; ++row)
|
|
{
|
|
bool set = g.grid.back[row][col] == CELL_SET;
|
|
|
|
if (set != was_set)
|
|
{
|
|
was_set = set;
|
|
++index;
|
|
}
|
|
|
|
if (set)
|
|
++g.col_counts[col][index];
|
|
}
|
|
}
|
|
}
|
|
|
|
RETRO_API void retro_init(void)
|
|
{
|
|
memset(&g, 0, sizeof(g));
|
|
g.first_run = true;
|
|
|
|
const char *chal =
|
|
" "
|
|
" ## # "
|
|
" ## # "
|
|
" # # "
|
|
" # # # "
|
|
" # "
|
|
" ## # "
|
|
" ## # "
|
|
" ";
|
|
|
|
load_challenge(chal);
|
|
}
|
|
|
|
RETRO_API void retro_deinit(void) { }
|
|
|
|
|
|
RETRO_API void retro_reset(void)
|
|
{
|
|
|
|
}
|
|
|
|
RETRO_API bool retro_load_game(const struct retro_game_info *game)
|
|
{
|
|
uint_env(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, RETRO_PIXEL_FORMAT_RGB565);
|
|
return true;
|
|
}
|
|
|
|
RETRO_API void retro_unload_game(void)
|
|
{
|
|
|
|
}
|
|
|
|
RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device)
|
|
{
|
|
|
|
}
|
|
|
|
RETRO_API void retro_get_system_info(struct retro_system_info *info)
|
|
{
|
|
info->library_name = "cruzes";
|
|
info->library_version = "0.1";
|
|
info->need_fullpath = false;
|
|
info->valid_extensions = "";
|
|
}
|
|
|
|
RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info)
|
|
{
|
|
info->geometry.base_width = SCREEN_WIDTH;
|
|
info->geometry.base_height = SCREEN_HEIGHT;
|
|
info->geometry.aspect_ratio = (double)info->geometry.base_width / info->geometry.base_height;
|
|
info->geometry.max_width = info->geometry.max_height = MAX(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
info->timing.fps = 60;
|
|
info->timing.sample_rate = 0.0;
|
|
}
|
|
|
|
RETRO_API unsigned retro_api_version(void) { return RETRO_API_VERSION; }
|
|
|
|
RETRO_API void retro_set_video_refresh(retro_video_refresh_t p) { video_cb = p; }
|
|
RETRO_API void retro_set_audio_sample(retro_audio_sample_t p) { }
|
|
RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t p) { }
|
|
RETRO_API void retro_set_input_poll(retro_input_poll_t p) { poll_cb = p; }
|
|
RETRO_API void retro_set_input_state(retro_input_state_t p) { input_cb = p; }
|
|
RETRO_API void retro_set_environment(retro_environment_t p)
|
|
{
|
|
env_cb = p;
|
|
bool_env(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, true);
|
|
}
|
|
|
|
RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RETRO_API unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
|
|
RETRO_API size_t retro_serialize_size(void) { return 0; }
|
|
RETRO_API bool retro_serialize(void *data, size_t size) { return false; }
|
|
RETRO_API bool retro_unserialize(const void *data, size_t size) { return false; }
|
|
RETRO_API void retro_cheat_reset(void) { }
|
|
RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code) { }
|
|
RETRO_API void *retro_get_memory_data(unsigned id) { return NULL; }
|
|
RETRO_API size_t retro_get_memory_size(unsigned id) { return 0; }
|