summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SUBDIRS2
-rw-r--r--apps/plugins/bitmaps/native/SOURCES4
-rw-r--r--apps/plugins/bitmaps/native/xracer_sprites.bmpbin0 -> 230522 bytes
-rw-r--r--apps/plugins/xracer/README79
-rw-r--r--apps/plugins/xracer/SOURCES7
-rw-r--r--apps/plugins/xracer/compat.h61
-rw-r--r--apps/plugins/xracer/generator.c202
-rw-r--r--apps/plugins/xracer/generator.h46
-rw-r--r--apps/plugins/xracer/graphics.c275
-rw-r--r--apps/plugins/xracer/graphics.h48
-rw-r--r--apps/plugins/xracer/main.c200
-rw-r--r--apps/plugins/xracer/map.c130
-rw-r--r--apps/plugins/xracer/map.h47
-rw-r--r--apps/plugins/xracer/maps.c15
-rw-r--r--apps/plugins/xracer/maps.h3
-rw-r--r--apps/plugins/xracer/road.h48
-rw-r--r--apps/plugins/xracer/sprite.c26
-rw-r--r--apps/plugins/xracer/sprite.h42
-rw-r--r--apps/plugins/xracer/util.c170
-rw-r--r--apps/plugins/xracer/util.h66
-rw-r--r--apps/plugins/xracer/xracer.h69
-rw-r--r--apps/plugins/xracer/xracer.make27
23 files changed, 1568 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index fd7a49a..57a95b6 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -134,5 +134,6 @@ wavrecord,apps
wavview,viewers
wormlet,games
xobox,games
+xracer,games
xworld,games
zxbox,viewers
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index d02073e..6213242 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -12,6 +12,8 @@ clock
/* For all targets with a bitmap display */
#ifdef HAVE_LCD_BITMAP
+xracer
+
/* XWorld only supports color horizontal stride LCDs /for now/ ;) */
#if defined(HAVE_LCD_COLOR) && \
(!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index 90376d8..91a2e01 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -991,4 +991,8 @@ resistor.128x128x16.bmp
resistor.68x20x16.bmp
#endif
+/* XRacer */
+/* these sprites are one-size-fits-all */
+xracer_sprites.bmp
+
#endif /* HAVE_LCD_BITMAP */
diff --git a/apps/plugins/bitmaps/native/xracer_sprites.bmp b/apps/plugins/bitmaps/native/xracer_sprites.bmp
new file mode 100644
index 0000000..a88eca6
--- /dev/null
+++ b/apps/plugins/bitmaps/native/xracer_sprites.bmp
Binary files differ
diff --git a/apps/plugins/xracer/README b/apps/plugins/xracer/README
new file mode 100644
index 0000000..8b6312c
--- /dev/null
+++ b/apps/plugins/xracer/README
@@ -0,0 +1,79 @@
+This file contains some quick notes about this plugin.
+======================================================
+
+Some useful links:
+------------------
+
+<http://codeincomplete.com/projects/racer/> - a very good tutorial on building a similar game in Javascript
+
+<http://www.extentofthejam.com/pseudo/> - excellent page on pseudo-3d
+
+Terminology:
+------------
+
+In the code, a "fixed-point" number is one that has a nonzero number of fractional bits.
+When dealing with these numbers, you must be careful to convert all operands to fixed-point, and use the correct function/macro to replace the operators.
+In the code, I've tried to make all fixed-point numbers of type 'long.'
+An "integer" is a number with zero fractional bits.
+
+(NOTE: I'm not sure if this "proper" terminology)
+
+Code structure:
+---------------
+
+generator.c: random map generator
+
+graphics.c: this is where almost all of the rendering takes place
+
+main.c: main loop
+
+sprite.c: static sprite data (offsets, dimensions)
+
+util.c miscellaneous functions, also contains FOV calculation code
+
+Proposed map formats:
+---------------------
+
+There will be two map formats: one that can be compiled into the executable ("Internal format"), and one that can be loaded from a file ("Interchange format").
+
+Internal format:
+----------------
+
+This format is represented as a series of struct road_section's.
+
+struct road_section {
+ uchar type;
+ uint32 len;
+ int slope;
+ long curve; // <<< this is fixed-point with 8 fracbits
+};
+
+'type' can be any of the following:
+ 0: constant segment - all 3 parameters used
+ 1: up-hill - an up hill is added. actual length is len + 2*slope due to enters and exits
+ 2: down-hill - a down hill is added. actual length is len + 2*slope due to enters and exits
+
+Endianness is platform-dependent in the Internal format.
+
+Interchange format:
+-------------------
+
+NOTE: all numbers are BIG-ENDIAN unless otherwise specified!
+
+This format is essentially the Internal format serialized into a file, but with some minor differences.
+
+File extension: .xrm
+
+Current format version number: 0x0000
+
+- File header - 8 bytes
+ - 0x00-0x04: 'XMaP'
+ - 0x05-0x06: version number (see above)
+ - 0x07-0x08: 0xFF padding
+- Data blocks - 15 bytes each (corresponds to the "sections" of the Internal format)
+ - 0x00 : type
+ - 0x01-0x04: length
+ - 0x05-0x08: slope (signed two's complement)
+ - 0x09-0x0C: curve (signed two's complement, fixed-point with 8 fractional bits)
+ - 0x0D-0x0E: CRC16-CCITT of data from 0x0-0xC
+ - 0x0F : CRC high byte XOR CRC low byte
diff --git a/apps/plugins/xracer/SOURCES b/apps/plugins/xracer/SOURCES
new file mode 100644
index 0000000..4f08451
--- /dev/null
+++ b/apps/plugins/xracer/SOURCES
@@ -0,0 +1,7 @@
+main.c
+generator.c
+graphics.h
+map.c
+maps.c
+sprite.c
+util.c
diff --git a/apps/plugins/xracer/compat.h b/apps/plugins/xracer/compat.h
new file mode 100644
index 0000000..f4507d5
--- /dev/null
+++ b/apps/plugins/xracer/compat.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#if (LCD_DEPTH < 4)
+#include "lib/grey.h"
+#else
+#include "lib/xlcd.h"
+#endif
+
+/* this file contains some #defines to make this as platform-neutral as possible */
+#if (LCD_DEPTH < 4)
+/* greylib API */
+
+#define FILL_TRI(x1, y1, x2, y2, x3, y3) grey_filltriangle(x1, y1, x2, y2, x3, y3)
+#define FILL_RECT(x, y, w, h) grey_fillrect(x, y, w, h)
+#define DRAW_HLINE(x1, x2, y) grey_hline(x1, x2, y)
+#define LCD_RGBPACK(r, g, b) GREY_BRIGHTNESS(((r+g+b)/3))
+#define CLEAR_DISPLAY() grey_clear_display()
+#define SET_FOREGROUND(x) grey_set_foreground(x)
+#define SET_BACKGROUND(x) grey_set_background(x)
+#undef LCD_BLACK
+#undef LCD_WHITE
+#define LCD_BLACK GREY_BLACK
+#define LCD_WHITE GREY_WHITE
+#define LCD_UPDATE() grey_update()
+#define PUTSXY(x, y, s) grey_putsxy(x, y, s)
+#define RGB_UNPACK_RED(x) (x)
+#define RGB_UNPACK_GREEN(x) (x)
+#define RGB_UNPACK_BLUE(x) (x)
+
+#else
+/* rockbox API */
+
+#define FILL_TRI(x1, y1, x2, y2, x3, y3) xlcd_filltriangle(x1, y1, x2, y2, x3, y3)
+#define FILL_RECT(x, y, w, h) rb->lcd_fillrect(x, y, w, h)
+#define DRAW_HLINE(x1, x2, y) rb->lcd_hline(x1, x2, y)
+#define CLEAR_DISPLAY() rb->lcd_clear_display()
+#define SET_FOREGROUND(x) rb->lcd_set_foreground(x)
+#define SET_BACKGROUND(x) rb->lcd_set_background(x)
+#define LCD_UPDATE() rb->lcd_update()
+#define PUTSXY(x, y, s) rb->lcd_putsxy(x, y, s)
+
+#endif
diff --git a/apps/plugins/xracer/generator.c b/apps/plugins/xracer/generator.c
new file mode 100644
index 0000000..d62594b
--- /dev/null
+++ b/apps/plugins/xracer/generator.c
@@ -0,0 +1,202 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "plugin.h"
+
+#include "xracer.h"
+#include "compat.h"
+#include "generator.h"
+#include "graphics.h"
+#include "road.h"
+#include "sprite.h"
+#include "util.h"
+
+
+static int last_y=0;
+
+void gen_reset(void)
+{
+ last_y = 0;
+}
+
+/* add_road(): adds a section of road segments between s and s+len,
+ each with the properties specified */
+void add_road(struct road_segment *road, unsigned int road_length, unsigned int s, unsigned int len, long curve, int hill)
+{
+ unsigned int i;
+ for(i = s; i < s+len && i < road_length; ++i)
+ {
+ struct road_segment tmp;
+ tmp.idx = i;
+ tmp.p1_z = i * SEGMENT_LENGTH;
+ tmp.p2_z = (i + 1) * SEGMENT_LENGTH;
+
+ /* change the lines below for hills */
+ tmp.p1_y = last_y;
+ tmp.p2_y = last_y+hill;
+ last_y += hill;
+
+ /* change this line for curves */
+ tmp.curve = curve;
+
+ tmp.color = (i/RUMBLE_LENGTH)%2?ROAD_COLOR1:ROAD_COLOR2;
+ tmp.border_color = (i/RUMBLE_LENGTH)%2?BORDER_COLOR1:BORDER_COLOR2;
+ tmp.lanes = (i/RUMBLE_LENGTH)%2?LANES:1;
+ tmp.lane_color = LANE_COLOR;
+ tmp.grass_color = (i/RUMBLE_LENGTH)%2?GRASS_COLOR1:GRASS_COLOR2;
+ tmp.clip = 0;
+ tmp.sprites = NULL;
+ road[i] = tmp;
+ }
+ if(i > road_length)
+ {
+ warning("road segment out of bounds (i=%d, len=%d)", i, road_length);
+ }
+}
+
+/* enter_uphill(): adds a road section with slopes 0,1...slope-1 */
+/* assumes slope is positive, for negative slopes, use *_downhill */
+static inline void enter_uphill(struct road_segment *road, unsigned int road_length, unsigned int s, int slope, long curve)
+{
+ for(int i=0; i<slope; ++i)
+ {
+ add_road(road, road_length, s + i, 1, curve, i);
+ }
+}
+
+/* exit_hill(): opposite of enter_hill, adds a road section with slopes slope-1...0 */
+/* assumes slope is positive, for negative slopes, use *_downhill */
+static inline void exit_uphill(struct road_segment *road, unsigned int road_length, unsigned int s, int slope, long curve)
+{
+ int n = slope;
+ for(int i = 0; i<n; ++i, --slope)
+ {
+ add_road(road, road_length, s + i, 1, curve, slope);
+ }
+}
+
+/* add_uphill(): currently unused */
+/* assumes slope is positive, for negative slopes, use *_downhill */
+void add_uphill(struct road_segment *road, unsigned int road_length, unsigned int s, int slope, int hold, long curve)
+{
+ enter_uphill(road, road_length, s, slope, curve);
+ add_road(road, road_length, s+slope, hold, curve, slope);
+ exit_uphill(road, road_length, s+slope+hold, slope, curve);
+}
+
+/* enter_downhill: adds a road section with slopes 0,-1,...slope+1 */
+/* assumes slope is negative, for positive slopes use *_uphill */
+static inline void enter_downhill(struct road_segment *road, unsigned int road_length, unsigned int s, int slope, long curve)
+{
+ int n = 0;
+ for(int i=0; i<-slope;++i, --n)
+ {
+ add_road(road, road_length, s + i, 1, curve, n);
+ }
+}
+
+/* adds a road section with slopes slope, slope+1,...,-1 */
+static inline void exit_downhill(struct road_segment *road, unsigned int road_length, unsigned int s, int slope, long curve)
+{
+ int n = slope;
+ for(int i=0; i<-slope; ++i, ++n)
+ {
+ add_road(road, road_length, s + i, 1, curve, n);
+ }
+}
+
+void add_downhill(struct road_segment *road, unsigned int road_length, unsigned int s, int slope, int hold, long curve)
+{
+ enter_downhill(road, road_length, s, slope, curve);
+ add_road(road, road_length, s-slope, hold, curve, slope);
+ exit_downhill(road, road_length, s-slope+hold, slope, curve);
+}
+
+/* generate_random_road(): generates a random track road_length segments long */
+
+void generate_random_road(struct road_segment *road, unsigned int road_length, bool hills, bool curves)
+{
+ gen_reset();
+ rb->srand(*rb->current_tick);
+ int len;
+ for(unsigned int i = 0; i < road_length; i += len)
+ {
+ /* get an EVEN number */
+ len = (rb->rand()% 20 + 10) * 2;
+ if(i + len >= road_length)
+ len = road_length - i;
+
+ int r = rb->rand() % 12;
+
+ /* probability distribution */
+ /* 0, 1: uphill */
+ /* 2, 3: downhill */
+ /* 4, 5, 6, 7: straight */
+ /* 8, 9: left */
+ /* 10,11: right */
+ switch(r)
+ {
+ case 0:
+ case 1:
+ enter_uphill(road, road_length, i, 5, 0);
+ add_road(road, road_length, i+5, len-10, 0, ( hills ? 5 : 0));
+ exit_uphill(road, road_length, i+len-5, 5, 0);
+ break;
+ case 2:
+ case 3:
+ enter_downhill(road, road_length, i, -5, 0);
+ add_road(road, road_length, i+5, len-10, 0, (hills ? -5 : 0));
+ exit_downhill(road, road_length, i+len-5, -5, 0);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ add_road(road, road_length, i, len, 0, 0);
+ break;
+ case 8:
+ case 9:
+ add_road(road, road_length, i, len, (curves ? -1 << FRACBITS: 0), 0);
+ break;
+ case 10:
+ case 11:
+ add_road(road, road_length, i, len, (curves ? 1 << FRACBITS: 0), 0);
+ break;
+ }
+ }
+}
+
+void add_sprite(struct road_segment *seg, struct sprite_data_t *data)
+{
+ LOGF("adding sprite");
+ struct sprite_t *s = util_alloc(sizeof(struct sprite_t));
+ s->data = data;
+ s->next = NULL;
+ if(!seg->sprites)
+ {
+ seg->sprites = s;
+ return;
+ }
+ struct sprite_t *i = seg->sprites;
+ while(i->next)
+ i = i->next;
+ i->next = s;
+}
diff --git a/apps/plugins/xracer/generator.h b/apps/plugins/xracer/generator.h
new file mode 100644
index 0000000..306c3b9
--- /dev/null
+++ b/apps/plugins/xracer/generator.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef _GENERATOR_H_
+#define _GENERATOR_H_
+
+#include <stdbool.h>
+
+struct sprite_data_t;
+struct road_segment;
+
+void gen_reset(void);
+
+void add_road(struct road_segment *road, unsigned int road_length, unsigned int idx, unsigned int len, long curve, int hill);
+
+/* slope *must* be positive! for negative slopes, use add_downhill */
+void add_uphill(struct road_segment *road, unsigned int road_length, unsigned int idx, int slope, int hold, long curve);
+
+/* slope *must* be negative! for positive slopes, use add_uphill */
+void add_downhill(struct road_segment *road, unsigned int road_length, unsigned int idx, int slope, int hold, long curve);
+
+void generate_random_road(struct road_segment *road, unsigned int road_length, bool hills, bool curves);
+
+/* appends a sprite to the end of the linked list of sprites */
+/* data must be static! */
+void add_sprite(struct road_segment *seg, struct sprite_data_t *data);
+
+#endif
diff --git a/apps/plugins/xracer/graphics.c b/apps/plugins/xracer/graphics.c
new file mode 100644
index 0000000..c1c5f92
--- /dev/null
+++ b/apps/plugins/xracer/graphics.c
@@ -0,0 +1,275 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "plugin.h"
+
+#include "fixedpoint.h"
+#include "lib/pluginlib_bmp.h"
+
+#include "pluginbitmaps/xracer_sprites.h"
+
+#include "xracer.h"
+#include "compat.h"
+#include "graphics.h"
+#include "road.h"
+#include "sprite.h"
+#include "util.h"
+
+/* fill poly takes 4 screenspace coordinates and fills them */
+/* order of arguments matters: */
+/* (x1, y1) (x2, y2) */
+/* (x3, y3) (x4, y4) */
+static inline void fill_poly(int x1, int y1,
+ int x2, int y2,
+ int x3, int y3,
+ int x4, int y4,
+ unsigned color)
+{
+ SET_FOREGROUND(color);
+ FILL_TRI(x1, y1, x2, y2, x3, y3);
+ FILL_TRI(x1, y1, x3, y3, x4, y4);
+}
+
+/* project(): performs 3d projection of pt_3d onto pt_2d given camera parameters */
+/* this takes arguments by value so as to allow easy changing of camera positions at render time*/
+
+static inline int project(int cam_x, int cam_y, int cam_z, int camera_depth, struct point_3d *pt_3d, struct point_2d *pt_2d, float *scale_return)
+{
+ /* calculate the coordinates relative to the camera */
+ int rel_x, rel_y, rel_z;
+ rel_x = pt_3d->x - cam_x;
+ rel_y = pt_3d->y - cam_y;
+ rel_z = pt_3d->z - cam_z;
+ float scale = (float)camera_depth / rel_z;
+ pt_2d->x = (LCD_WIDTH/2) + (scale * rel_x * (LCD_WIDTH/2));
+ pt_2d->y = (LCD_WIDTH/2) - (scale * rel_y * (LCD_HEIGHT/2));
+ pt_2d->w = scale * ROAD_WIDTH * (LCD_WIDTH/2);
+ if(scale_return)
+ *scale_return=scale;
+ return rel_z;
+}
+
+static inline int border_width(int projected_road_width, int lanes)
+{
+ return projected_road_width/MAX(6, 2*lanes);
+}
+
+static inline int lane_marker_width(int projected_road_width, int lanes)
+{
+ return projected_road_width/MAX(32, 8*lanes);
+}
+
+/* fill_gradient(): draws a gradient across the whole screen from top to bottom,
+ changing from top_color to bottom_color */
+
+static void fill_gradient(int top, unsigned top_color, int bottom, unsigned bottom_color)
+{
+ long top_r, top_g, top_b;
+ top_r = RGB_UNPACK_RED(top_color) << FRACBITS;
+ top_g = RGB_UNPACK_GREEN(top_color) << FRACBITS;
+ top_b = RGB_UNPACK_BLUE(top_color) << FRACBITS;
+
+ long bottom_r, bottom_g, bottom_b;
+ bottom_r = RGB_UNPACK_RED(bottom_color) << FRACBITS;
+ bottom_g = RGB_UNPACK_GREEN(bottom_color) << FRACBITS;
+ bottom_b = RGB_UNPACK_BLUE(bottom_color) << FRACBITS;
+
+ bottom <<= FRACBITS;
+ top <<= FRACBITS;
+ long r_step = fp_div((bottom_r-top_r), (bottom-top), FRACBITS);
+ long g_step = fp_div((bottom_g-top_g), (bottom-top), FRACBITS);
+ long b_step = fp_div((bottom_b-top_b), (bottom-top), FRACBITS);
+
+ long r = top_r;
+ long g = top_g;
+ long b = top_b;
+
+ bottom >>= FRACBITS;
+ top >>= FRACBITS;
+
+ for(int i = top; i<bottom; ++i)
+ {
+ SET_FOREGROUND(LCD_RGBPACK( ROUND(r), ROUND(g), ROUND(b) ));
+ DRAW_HLINE(0, LCD_WIDTH, i);
+ r += r_step;
+ g += g_step;
+ b += b_step;
+ }
+}
+
+/* draws a segment on screen given its 2d screen coordinates and other data */
+
+static inline void render_segment(struct point_2d *p1, struct point_2d *p2,
+ unsigned road_color, unsigned border_color,
+ int lanes, unsigned lane_color, unsigned grass_color)
+{
+ int border_1 = border_width(p1->w, lanes);
+ int border_2 = border_width(p2->w, lanes);
+
+ /* draw grass first */
+ SET_FOREGROUND(grass_color);
+ FILL_RECT(0, p2->y, LCD_WIDTH, p1->y - p2->y);
+
+ /* draw borders */
+ fill_poly(p1->x-p1->w-border_1, p1->y, p1->x-p1->w, p1->y,
+ p2->x-p2->w, p2->y, p2->x-p2->w-border_2, p2->y, border_color);
+
+ fill_poly(p1->x+p1->w+border_1, p1->y, p1->x+p1->w, p1->y,
+ p2->x+p2->w, p2->y, p2->x+p2->w+border_2, p2->y, border_color);
+
+ /* draw road */
+ fill_poly(p2->x-p2->w, p2->y, p2->x+p2->w, p2->y, p1->x+p1->w, p1->y, p1->x-p1->w, p1->y, road_color);
+
+ /* draw lanes */
+ if(lanes > 1)
+ {
+ int lane_1 = lane_marker_width(p1->w, lanes);
+ int lane_2 = lane_marker_width(p2->w, lanes);
+
+ int lane_w1, lane_w2, lane_x1, lane_x2;
+ lane_w1 = p1->w*2/lanes;
+ lane_w2 = p2->w*2/lanes;
+ lane_x1 = p1->x - p1->w + lane_w1;
+ lane_x2 = p2->x - p2->w + lane_w2;
+ for(int i=1; i<lanes; lane_x1 += lane_w1, lane_x2 += lane_w2, ++i)
+ {
+ fill_poly(lane_x1 - lane_1/2, p1->y, lane_x1 + lane_1/2, p1->y,
+ lane_x2 + lane_2/2, p2->y, lane_x2 - lane_2/2, p2->y, lane_color);
+ }
+ }
+}
+
+/* project_segment(): does the 3d calculations on a road segment */
+
+static inline bool project_segment(struct road_segment *base, struct road_segment *seg, int x, int dx,
+ struct camera_t *camera, struct point_2d *p1, struct point_2d *p2, int *maxy,
+ int road_length)
+{
+ bool looped = (seg->idx < base->idx);
+ seg->clip = *maxy;
+
+ /* future optimization: do one projection per segment because segment n's p2 is the same as segments n+1's p1*/
+
+ /* input points (3d space) */
+
+ struct point_3d p1_3d={-x, seg->p1_y, seg->p1_z};
+ struct point_3d p2_3d={-x-dx, seg->p2_y, seg->p2_z};
+
+ int camera_offset = (looped ? road_length * SEGMENT_LENGTH - camera->pos.z : 0);
+
+ /* 3d project the 2nd point FIRST because its results can be used to save some work */
+
+ int p2_cz = project(camera->pos.x, camera->pos.y, camera->pos.z - camera_offset, camera->depth, &p2_3d, p2, NULL);
+
+ /* decide whether or not to draw this segment */
+
+ if(p2_cz <= camera->depth || /* behind camera */
+ p2->y >= *maxy) /* clipped */
+ {
+ return false;
+ }
+
+ /* nothing is drawn below this */
+ *maxy = p2->y;
+
+ /* now 3d project the first point */
+
+ /* save the scale factor to draw sprites */
+ project(camera->pos.x, camera->pos.y, camera->pos.z - camera_offset, camera->depth, &p1_3d, p1, &seg->p1_scale);
+
+ if(p2->y >= p1->y) /* backface cull */
+ {
+ return false;
+ }
+ return true;
+}
+
+/* render(): The primary render driver function
+ *
+ * calculates segment coordinates and calls render_segment to draw the segment on-screen
+ * also handles the faking of curves
+ */
+
+void render(struct camera_t *camera, struct road_segment *road, unsigned int road_length, int camera_height)
+{
+ CLEAR_DISPLAY();
+
+ fill_gradient(0, SKY_COLOR1, LCD_HEIGHT, SKY_COLOR2);
+
+ struct road_segment *base = FIND_SEGMENT(camera->pos.z, road, road_length);
+
+ long base_percent = fp_div((camera->pos.z % SEGMENT_LENGTH) << FRACBITS, SEGMENT_LENGTH << FRACBITS, FRACBITS);
+ long dx = - fp_mul(base->curve, base_percent, FRACBITS);
+ long x = 0;
+
+ /* clipping height, nothing is drawn below this */
+ int maxy = LCD_HEIGHT;
+
+ /* move the camera to a fixed point above the road */
+ /* interpolate so as to prevent jumpy camera movement on hills */
+ camera->pos.y = INTERPOLATE(base->p1_y, base->p2_y, base_percent) + camera_height;
+
+ for(int i = 0; i < DRAW_DIST; ++i)
+ {
+ struct road_segment *seg = &road[(base->idx + i) % road_length];
+
+ if(project_segment(base, seg, ROUND(x), ROUND(dx), camera, &seg->p1, &seg->p2, &maxy, road_length))
+ render_segment(&seg->p1, &seg->p2, seg->color, seg->border_color, seg->lanes, seg->lane_color, seg->grass_color);
+ /* curve calculation */
+ x += dx;
+ dx += seg->curve; /* seg->curve is already FP */
+ }
+ /* draw sprites */
+ unsigned char scale_buffer[LCD_WIDTH * LCD_HEIGHT * sizeof(fb_data)];
+#if (LCD_DEPTH >= 4)
+ for(int i = DRAW_DIST - 1; i > 0; --i)
+ {
+ struct road_segment *seg = &road[(base->idx + i) % road_length];
+ struct sprite_t *iter = seg->sprites;
+ while(iter)
+ {
+ struct sprite_data_t *sprite = iter->data;
+ float scale = seg->p1_scale;
+ if(seg->p1.x && seg->p1.y)
+ {
+ struct bitmap in = {
+ .height = sprite->h,
+ .width = sprite->w,
+ .data = (unsigned char*)(xracer_sprites + STRIDE(SCREEN_MAIN, BMPWIDTH_xracer_sprites, BMPHEIGHT_xracer_sprites) * sprite->y + sprite->x)
+ };
+ struct bitmap out = {
+ .height = sprite->h * scale,
+ .width = sprite->w * scale,
+ .data = scale_buffer
+ };
+ if(sprite->h * scale > 1 && sprite->w * scale > 1)
+ {
+ simple_resize_bitmap(&in, &out);
+ rb->lcd_bitmap_transparent((fb_data*)scale_buffer,
+ seg->p1.x - LCD_WIDTH/2, seg->p1.y - LCD_WIDTH/2,
+ sprite->w * scale, sprite->h * scale);
+ }
+ }
+ iter = iter->next;
+ }
+ }
+#endif
+}
diff --git a/apps/plugins/xracer/graphics.h b/apps/plugins/xracer/graphics.h
new file mode 100644
index 0000000..aeecf0b
--- /dev/null
+++ b/apps/plugins/xracer/graphics.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef _GRAPHICS_H_
+#define _GRAPHICS_H_
+
+/* some basic types */
+
+struct point_3d {
+ int x;
+ int y;
+ int z;
+};
+
+struct camera_t {
+ struct point_3d pos;
+ int depth;
+};
+
+struct point_2d {
+ int x;
+ int y;
+ int w;
+};
+
+struct road_segment;
+
+void render(struct camera_t *camera, struct road_segment *road, unsigned int road_length, int camera_height);
+
+#endif
diff --git a/apps/plugins/xracer/main.c b/apps/plugins/xracer/main.c
new file mode 100644
index 0000000..b0127be
--- /dev/null
+++ b/apps/plugins/xracer/main.c
@@ -0,0 +1,200 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * This code draws heavily on
+ * Jake Gordon's "How to build a racing game" tutorial at
+ *
+ * <http://codeincomplete.com/projects/racer/>
+ *
+ * and
+ *
+ * Louis Gorenfield's "Lou's Pseudo 3d Page" at
+ *
+ * <http://www.extentofthejam.com/pseudo/>
+ *
+ * Thanks!
+ */
+
+#include "plugin.h"
+
+#include "xracer.h"
+#include "compat.h"
+#include "generator.h"
+#include "graphics.h"
+#include "map.h"
+#include "maps.h"
+#include "road.h"
+#include "sprite.h"
+#include "util.h"
+
+#include "lib/helper.h"
+#include "lib/pluginlib_actions.h"
+#include "lib/pluginlib_exit.h"
+
+#include "fixedpoint.h" /* only used for FOV computation */
+
+/* can move to audiobuf if too big... */
+struct road_segment road[MAX_ROAD_LENGTH];
+
+/* this can be changed to allow for variable-sized maps */
+unsigned int road_length=MAX_ROAD_LENGTH;
+
+int camera_height = CAMERA_HEIGHT;
+
+void generate_test_road(void)
+{
+ gen_reset();
+ add_road(road, road_length, 0, road_length, 0, 0);
+ for(unsigned int i = 0; i < road_length; ++i)
+ add_sprite(road + i, &sprites[0]);
+}
+
+void do_early_init(void)
+{
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ /* GIMME DAT RAW POWAH!!! */
+ rb->cpu_boost(true);
+#endif
+
+ /* disable backlight timeout */
+ backlight_ignore_timeout();
+
+ init_alloc();
+}
+
+#if LCD_DEPTH < 4
+struct _grey_info _grey_info;
+#endif
+
+void do_late_init(void)
+{
+#if (LCD_DEPTH < 4)
+ /* greylib init */
+ size_t buf_sz = util_alloc_remaining();
+ if(!grey_init(util_alloc(buf_sz), buf_sz,
+ GREY_BUFFERED|GREY_RAWMAPPED|GREY_ON_COP,
+ LCD_WIDTH, LCD_HEIGHT, NULL))
+ {
+ error("grey init failed");
+ }
+ grey_show(true);
+#endif
+}
+
+enum plugin_status do_flythrough(void)
+{
+ struct camera_t camera;
+ camera.depth = camera_calc_depth(CAMERA_FOV);
+
+ LOGF("camera depth: %d", camera.depth);
+
+ camera.pos.x = 0;
+ /* y is automatically calculated */
+ camera.pos.z = 0;
+
+ //generate_test_road();
+
+ //road_length = load_map(road, MAX_ROAD_LENGTH, loop_map, ARRAYLEN(loop_map));
+
+ road_length = load_external_map(road, MAX_ROAD_LENGTH, "/output.xrm");
+
+ if(road_length == (unsigned int)-1)
+ {
+ warning("Failed to load map, using random map");
+
+ road_length = MAX_ROAD_LENGTH;
+
+ generate_random_road(road, road_length, HILLS, CURVES);
+ }
+
+ long last_timestamp = *rb->current_tick;
+
+ do_late_init();
+
+ while(1)
+ {
+ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
+ int button = pluginlib_getaction(0, plugin_contexts, ARRAYLEN(plugin_contexts));
+ switch(button)
+ {
+ case PLA_UP:
+ case PLA_UP_REPEAT:
+ camera_height+=MANUAL_SPEED;
+ break;
+ case PLA_DOWN:
+ case PLA_DOWN_REPEAT:
+ camera_height-=MANUAL_SPEED;
+ break;
+ case PLA_LEFT:
+ case PLA_LEFT_REPEAT:
+ camera.pos.x-=MANUAL_SPEED;
+ break;
+ case PLA_RIGHT:
+ case PLA_RIGHT_REPEAT:
+ camera.pos.x+=MANUAL_SPEED;
+ break;
+ case PLA_CANCEL:
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ rb->cpu_boost(false);
+#endif
+ return PLUGIN_OK;
+ default:
+ exit_on_usb(button);
+ break;
+ }
+ camera.pos.z += 512;
+ /* loop the track right before going off the "end" */
+ camera.pos.z %= (road_length - DRAW_DIST) * SEGMENT_LENGTH;
+
+ render(&camera, road, road_length, camera_height);
+
+ SET_FOREGROUND(LCD_WHITE);
+ SET_BACKGROUND(LCD_BLACK);
+ int dt = *rb->current_tick - last_timestamp;
+ char buf[16];
+ //rb->snprintf(buf, sizeof(buf), "FPS: %ld", HZ/(!dt?1:dt));
+ rb->snprintf(buf, sizeof(buf), "DT: %d", dt);
+ PUTSXY(0, 0, buf);
+
+ LCD_UPDATE();
+
+ //rb->sleep((HZ/100)-dt);
+ rb->yield();
+
+ last_timestamp = *rb->current_tick;
+ }
+}
+
+/* plugin_start(): plugin entry point */
+/* contains main loop */
+enum plugin_status plugin_start(const void *param)
+{
+ (void) param;
+
+ do_early_init();
+
+ enum plugin_status rc = do_flythrough();
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ rb->cpu_boost(false);
+#endif
+ return rc;
+}
diff --git a/apps/plugins/xracer/map.c b/apps/plugins/xracer/map.c
new file mode 100644
index 0000000..6900dab
--- /dev/null
+++ b/apps/plugins/xracer/map.c
@@ -0,0 +1,130 @@
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "generator.h"
+#include "map.h"
+#include "util.h"
+
+static bool add_section(struct road_segment *road, unsigned int max_road_length, unsigned int *back, struct road_section *map)
+{
+ bool ret = true;
+ switch(map->type)
+ {
+ case 0:
+ /* constant segment */
+ add_road(road, max_road_length, *back, map->len, map->curve, map->slope);
+ *back += map->len;
+ break;
+ case 1:
+ /* up-hill */
+ add_uphill(road, max_road_length, *back, map->slope, map->len, map->curve);
+ *back += map->len + 2*map->slope;
+ break;
+ case 2:
+ add_downhill(road, max_road_length, *back, map->slope, map->len, map->curve);
+ *back += map->len + 2*map->slope;
+ break;
+ default:
+ warning("invalid type id");
+ ret = false;
+ break;
+ }
+ return ret;
+}
+
+unsigned load_map(struct road_segment *road, unsigned int max_road_length, struct road_section *map, unsigned int map_length)
+{
+ gen_reset();
+ unsigned int back = 0;
+ for(unsigned int i=0;i<map_length;++i)
+ {
+ add_section(road, max_road_length, &back, &map[i]);
+ }
+ return back;
+}
+
+unsigned load_external_map(struct road_segment *road, unsigned int max_road_length, const char *filename)
+{
+ int fd = rb->open(filename, O_RDONLY, 0666);
+ if(fd < 0)
+ {
+ warning("Map file could not be opened");
+ return -1;
+ }
+
+ /* check the header */
+ static const unsigned char cmp_header[8] = {'X', 'M', 'a', 'P',
+ ((MAP_VERSION & 0xFF00) >> 8),
+ ((MAP_VERSION & 0x00FF) >> 0),
+ 0xFF, 0xFF };
+ unsigned char header[8];
+ int ret = rb->read(fd, header, sizeof(header));
+ if(ret != sizeof(header) || rb->memcmp(header, cmp_header, sizeof(header)) != 0)
+ {
+ warning("Malformed map header (version mismatch?)");
+ return -1;
+ }
+
+ /* read data */
+ unsigned char data_buf[16];
+ unsigned int back = 0;
+ bool fail = false;
+
+ gen_reset();
+
+ do {
+
+ ret = rb->read(fd, data_buf, sizeof(data_buf));
+ if(ret != sizeof(data_buf))
+ {
+ break;
+ }
+
+ uint16_t calc_crc16 = crc16_ccitt(data_buf, 12, 0, 0xFFFF);
+ uint8_t calc_xor_value = ((calc_crc16 & 0xFF00) >> 8 ) ^ (calc_crc16 & 0xFF);
+
+ uint16_t crc16 = (data_buf[0x0D] << 8) | (data_buf[0x0E]);
+ uint8_t xor_value = data_buf[0x0F];
+ if((calc_crc16 != crc16))
+ {
+ warning("Bad checksum");
+ fail = true;
+ }
+ if(calc_xor_value != xor_value)
+ {
+ warning("Bad XOR value");
+ fail = true;
+ }
+
+ struct road_section section =
+ {
+ data_buf[0],
+ READ_BE_UINT32(&data_buf[1]),
+ READ_BE_UINT32(&data_buf[5]),
+ READ_BE_UINT32(&data_buf[9])
+ };
+
+ bool status = add_section(road, max_road_length, &back, &section);
+
+ if(!status)
+ {
+ warning("Unknown map section type");
+ fail = true;
+ }
+
+ } while(ret == sizeof(data_buf));
+
+ /* last number of bytes read was not zero, this indicates failure */
+ if(ret)
+ {
+ warning("Data too long");
+ fail = true;
+ }
+
+ rb->close(fd);
+
+ if(fail)
+ return -1;
+ else
+ return back;
+}
diff --git a/apps/plugins/xracer/map.h b/apps/plugins/xracer/map.h
new file mode 100644
index 0000000..1681b12
--- /dev/null
+++ b/apps/plugins/xracer/map.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef _MAP_H_
+#define _MAP_H_
+
+#include <stdint.h>
+
+#define MAP_VERSION 0x0001
+
+struct road_segment;
+
+struct road_section {
+ unsigned char type;
+ uint32_t len;
+ int32_t slope;
+
+ /* FP, 8 fracbits */
+ long curve;
+};
+
+/* takes a map in internal format and converts it to road_segment's */
+
+unsigned int load_map(struct road_segment*, unsigned max_road_length, struct road_section*, unsigned map_len);
+
+/* loads a map in interchange format from a file */
+unsigned load_external_map(struct road_segment*, unsigned int max_road_length, const char*);
+
+#endif
diff --git a/apps/plugins/xracer/maps.c b/apps/plugins/xracer/maps.c
new file mode 100644
index 0000000..770f3a6
--- /dev/null
+++ b/apps/plugins/xracer/maps.c
@@ -0,0 +1,15 @@
+#include "xracer.h"
+#include "maps.h"
+
+struct road_section loop_map[10] = {
+ { 0, 300, 0, 0 },
+ { 0, 100, 0, 1 << (FRACBITS) },
+ { 0, 200, 0, 0 },
+ { 0, 100, 0, 1 << (FRACBITS) },
+ { 0, 600, 0, 0 },
+ { 0, 100, 0, 1 << (FRACBITS) },
+ { 0, 200, 0, 0 },
+ { 0, 100, 0, 1 << (FRACBITS) },
+ { 0, 300, 0, 0 },
+ { 0, DRAW_DIST, 0, 0}
+};
diff --git a/apps/plugins/xracer/maps.h b/apps/plugins/xracer/maps.h
new file mode 100644
index 0000000..cab72d5
--- /dev/null
+++ b/apps/plugins/xracer/maps.h
@@ -0,0 +1,3 @@
+#include "map.h"
+
+struct road_section loop_map[10];
diff --git a/apps/plugins/xracer/road.h b/apps/plugins/xracer/road.h
new file mode 100644
index 0000000..2c1fe44
--- /dev/null
+++ b/apps/plugins/xracer/road.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef _ROAD_H_
+#define _ROAD_H_
+
+struct point_2d;
+struct sprite_t;
+
+struct road_segment {
+ int idx;
+ int p1_z;
+ int p2_z;
+ int p1_y;
+ int p2_y;
+ struct point_2d p1;
+ struct point_2d p2;
+ float p1_scale; /* slow... */
+ long curve; /* fixed-point with FRACBITS fractional bits */
+ unsigned color;
+ unsigned border_color;
+ int lanes;
+ unsigned lane_color;
+ unsigned grass_color;
+ int clip;
+ /* linked list of sprites */
+ struct sprite_t *sprites;
+};
+
+#endif
diff --git a/apps/plugins/xracer/sprite.c b/apps/plugins/xracer/sprite.c
new file mode 100644
index 0000000..1ad6ee9
--- /dev/null
+++ b/apps/plugins/xracer/sprite.c
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "sprite.h"
+
+struct sprite_data_t sprites[] = {
+ {0, 0, 320, 240}, /* billboard 1 */
+};
diff --git a/apps/plugins/xracer/sprite.h b/apps/plugins/xracer/sprite.h
new file mode 100644
index 0000000..ae7f00e
--- /dev/null
+++ b/apps/plugins/xracer/sprite.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef _SPRITE_H_
+#define _SPRITE_H_
+
+struct sprite_data_t {
+ int x, y;
+ int w, h;
+};
+
+struct sprite_data_t sprites[1];
+
+/* the sprite_t object/struct/whatever should be as lightweight as possible */
+/* here it consists of just a pointer to the data */
+
+struct sprite_t {
+ struct sprite_t *next;
+ struct sprite_data_t *data;
+ /* offset ranges from -1 (FP) to 1 (FP) */
+ long offset;
+};
+
+#endif
diff --git a/apps/plugins/xracer/util.c b/apps/plugins/xracer/util.c
new file mode 100644
index 0000000..a518649
--- /dev/null
+++ b/apps/plugins/xracer/util.c
@@ -0,0 +1,170 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "plugin.h"
+#include "fixedpoint.h"
+#include "util.h"
+
+static void *plugin_buffer = NULL;
+static size_t bytes_left = -1;
+
+void init_alloc(void)
+{
+ if(!plugin_buffer)
+ {
+ plugin_buffer = rb->plugin_get_buffer(&bytes_left);
+ }
+}
+
+void *util_alloc(size_t sz)
+{
+ if(plugin_buffer)
+ {
+ void *ret = plugin_buffer;
+ plugin_buffer += sz;
+ if((signed long) bytes_left - (signed long) sz < 0)
+ {
+ error("Im all outta memoriez!!! :(");
+ return NULL;
+ }
+ bytes_left -= sz;
+ return ret;
+ }
+ else
+ {
+ warning("call to %s without init", __func__);
+ return NULL;
+ }
+}
+
+size_t util_alloc_remaining(void)
+{
+ if(plugin_buffer)
+ return bytes_left;
+ else
+ {
+ warning("call to %s without init", __func__);
+ return -1;
+ }
+}
+
+/* camera_calc_depth(): calculates the camera depth given its FOV by evaluating */
+/* LCD_WIDTH / tan(fov/2) */
+int camera_calc_depth(int fov)
+{
+ fov /= 2;
+ long fov_sin, fov_cos;
+ /* nasty (but fast) fixed-point math! */
+ fov_sin = fp14_sin(fov);
+ fov_cos = fp14_cos(fov);
+
+ long tan;
+ /* fp14_sin/cos has 14 fractional bits */
+ tan = fp_div(fov_sin, fov_cos, 14);
+
+ long width = LCD_WIDTH << 14;
+ return fp_div(width, tan, 14) >> 14;
+}
+
+void error_real(const char *msg, ...) {
+ char buf[256];
+ va_list va;
+ va_start(va, msg);
+ rb->vsnprintf(buf, sizeof(buf), msg, va);
+ va_end(va);
+ rb->splashf(HZ * 2, "ERROR: %s!", buf);
+ LOGF("ERROR: %s!", buf);
+ exit(-1);
+}
+
+/* this CRC code was adapted from <http://automationwiki.com/index.php?title=CRC-16-CCITT> */
+
+static uint16_t crc_table [256] = {
+
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5,
+ 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b,
+ 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
+ 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c,
+ 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
+ 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
+ 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
+ 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
+ 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5,
+ 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969,
+ 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
+ 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
+ 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03,
+ 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
+ 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6,
+ 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
+ 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb,
+ 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1,
+ 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,
+ 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
+ 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
+ 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447,
+ 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
+ 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2,
+ 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
+ 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827,
+ 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
+ 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0,
+ 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d,
+ 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
+ 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba,
+ 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
+ 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+uint16_t crc16_ccitt(unsigned char *data, size_t length, uint16_t seed, uint16_t final)
+{
+ size_t count;
+ unsigned int crc = seed;
+ unsigned int temp;
+
+ for (count = 0; count < length; ++count)
+ {
+ temp = (*data++ ^ (crc >> 8)) & 0xff;
+ crc = crc_table[temp] ^ (crc << 8);
+ }
+
+ return (uint16_t)(crc ^ final);
+}
+
+void warning_real(const char *msg, ...) {
+ char buf[256];
+ va_list va;
+ va_start(va, msg);
+ rb->vsnprintf(buf, sizeof(buf), msg, va);
+ va_end(va);
+ rb->splashf(HZ * 2, "WARNING: %s!", buf);
+ LOGF("WARNING: %s!", buf);
+}
diff --git a/apps/plugins/xracer/util.h b/apps/plugins/xracer/util.h
new file mode 100644
index 0000000..2112c88
--- /dev/null
+++ b/apps/plugins/xracer/util.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include <stdint.h>
+
+#include "xracer.h"
+#include "fixedpoint.h"
+
+/* finds the address of a segment corresponding to a z coordinate */
+#define FIND_SEGMENT(z, r, l) (&r[(int)((float)z/SEGMENT_LENGTH)%l])
+
+/* linearly interpolates a and b with percent having FRACBITS fractional bits */
+#define INTERPOLATE(a, b, percent) (((a << FRACBITS) + fp_mul((b<<FRACBITS)-(a<<FRACBITS), percent, FRACBITS)) >> FRACBITS)
+
+/* rounds a fixed-point number with FRACBITS fractional bits */
+/* returns an integer */
+/* adds 1/2 and truncates the fractional bits */
+#define ROUND(x) ((x+(1<<(FRACBITS-1)))>>FRACBITS)
+
+#ifdef ROCKBOX_LITTLE_ENDIAN
+#define READ_BE_UINT16(p) ((((const uint8_t*)p)[0] << 8) | ((const uint8_t*)p)[1])
+#define READ_BE_UINT32(p) ((((const uint8_t*)p)[0] << 24) | (((const uint8_t*)p)[1] << 16) | (((const uint8_t*)p)[2] << 8) | ((const uint8_t*)p)[3])
+#else
+#define READ_BE_UINT16(p) (*(const uint16_t*)p)
+#define READ_BE_UINT32(p) (*(const uint32_t*)p)
+#endif
+
+/* call this before using any alloc functions or the plugin buffer */
+void init_alloc(void);
+
+void *util_alloc(size_t);
+
+size_t util_alloc_remaining(void);
+
+/* camera_calc_depth(): calculates the camera depth given its FOV by evaluating */
+/* LCD_WIDTH / tan(fov/2) */
+int camera_calc_depth(int fov);
+
+void warning_real(const char *msg, ...);
+
+void error_real(const char *msg, ...);
+
+unsigned short crc16_ccitt(unsigned char *data, size_t length, unsigned short seed, unsigned short final);
+
+#define warning warning_real
+#define error error_real
+/*#define warning(msg, ...) warning_real("in function %s at %s:%d: %s", __func__, __FILE__, __LINE__, msg, ##__VA_ARGS__)
+ #define error(msg, ...) error_real("in function %s at %s:%d: %s", __func__, __FILE__, __LINE__, msg, ##__VA_ARGS__)*/
diff --git a/apps/plugins/xracer/xracer.h b/apps/plugins/xracer/xracer.h
new file mode 100644
index 0000000..967f948
--- /dev/null
+++ b/apps/plugins/xracer/xracer.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef _XRACER_H_
+#define _XRACER_H_
+
+/* miscellaneous #defines and data types */
+
+#include "plugin.h"
+
+/* colors */
+#define SKY_COLOR1 LCD_RGBPACK(0, 0x96, 0xff) /* top of sky gradient */
+#define SKY_COLOR2 LCD_RGBPACK(0xfe, 0xfe, 0xfe) /* bottom of sky gradient */
+#define GRASS_COLOR1 LCD_RGBPACK(0, 0xc5, 0)
+#define GRASS_COLOR2 LCD_RGBPACK(0, 0x9a, 0)
+#define ROAD_COLOR1 LCD_RGBPACK(120, 120, 120)
+#define ROAD_COLOR2 LCD_RGBPACK(80, 80, 80)
+#define BORDER_COLOR1 LCD_RGBPACK(240, 0, 0)
+#define BORDER_COLOR2 LCD_RGBPACK(240, 240, 240)
+#define LANE_COLOR LCD_RGBPACK(0xcc, 0xcc, 0xcc)
+
+/* road parameters */
+#define ROAD_WIDTH (LCD_WIDTH/2) /* actually half the road width for easier math */
+#define MAX_ROAD_LENGTH 4096
+#define SEGMENT_LENGTH (LCD_WIDTH * 12)
+
+/* road generator parameters */
+#define HILLS true
+#define CURVES true
+
+/* this specifies the length of each road and border color in segments */
+#define RUMBLE_LENGTH 3
+#define LANES 3
+
+/* camera parameters */
+#define DRAW_DIST 512
+#define CAMERA_FOV 120 /* in degrees */
+#define CAMERA_HEIGHT 100
+
+/* game parameters */
+/* currently this is how far the camera moves per keypress */
+#define MANUAL_SPEED 50
+
+/* number of bits to use for the fractional part of fixed-point numbers */
+/* note that FOV calculation always uses 14 by default */
+#define FRACBITS 8
+
+/* the number of fractional bits to use for high-precision numbers */
+#define HIPREC_FRACBITS 24
+
+#endif
diff --git a/apps/plugins/xracer/xracer.make b/apps/plugins/xracer/xracer.make
new file mode 100644
index 0000000..f669e01
--- /dev/null
+++ b/apps/plugins/xracer/xracer.make
@@ -0,0 +1,27 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+XRACERSRCDIR := $(APPSDIR)/plugins/xracer
+XRACERBUILDDIR := $(BUILDDIR)/apps/plugins/xracer
+
+ROCKS += $(XRACERBUILDDIR)/xracer.rock
+
+XRACER_SRC := $(call preprocess, $(XRACERSRCDIR)/SOURCES)
+XRACER_OBJ := $(call c2obj, $(XRACER_SRC))
+
+# add source files to OTHER_SRC to get automatic dependencies
+OTHER_SRC += $(XRACER_SRC)
+
+XRACERFLAGS = $(filter-out -O%,$(PLUGINFLAGS)) -O2
+
+$(XRACERBUILDDIR)/xracer.rock: $(XRACER_OBJ)
+
+$(XRACERBUILDDIR)/%.o: $(XRACERSRCDIR)/%.c $(wildcard (XRACERSRCDIR)/*.h) $(XRACERSRCDIR)/xracer.make
+ $(SILENT)mkdir -p $(dir $@)
+ $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(XRACERFLAGS) -c $< -o $@