Some bizarre stuff...

This commit is contained in:
Ilari Liusvaara 2013-03-02 16:45:53 +02:00
parent 03d1ec9aae
commit 5ec61c864f
41 changed files with 5165 additions and 2 deletions

View file

@ -9,4 +9,6 @@ struct controller_set
std::vector<port_type*> ports;
};
extern uint32_t magic_flags;
#endif

View file

@ -1,4 +1,4 @@
CORES=bsnes-legacy gambatte
CORES=bsnes-legacy gambatte sky
ALLFILES=__all__.files
ALLFLAGS=__all__.ldflags
CORES_FILES=$(patsubst %,%/$(ALLFILES),$(CORES))
@ -14,16 +14,21 @@ bsnes-legacy/$(ALLFILES): forcelook
gambatte/$(ALLFILES): forcelook
$(MAKE) -C gambatte
sky/$(ALLFILES): forcelook
$(MAKE) -C sky
.PRECIOUS: %.$(OBJECT_SUFFIX) %.files
precheck:
$(MAKE) -C bsnes-legacy precheck
$(MAKE) -C gambatte precheck
$(MAKE) -C sky precheck
clean:
rm -f *.$(OBJECT_SUFFIX) __all__.ldflags __all__.files
$(MAKE) -C bsnes-legacy clean
$(MAKE) -C gambatte clean
$(MAKE) -C sky clean
forcelook:
@true

View file

@ -738,6 +738,7 @@ namespace
old = SNES::interface;
SNES::interface = &my_interface_obj;
SNES::system.init();
magic_flags |= 1;
},
//Uninstall handler
[]() -> void { SNES::interface = old; },

View file

@ -374,7 +374,7 @@ namespace
return std::make_pair(max(512 / width, (uint32_t)1), max(448 / height, (uint32_t)1));
},
//Install handler
[]() -> void {},
[]() -> void { magic_flags |= 2; },
//Uninstall handler.
[]() -> void {},
//Emulate frame.

View file

@ -0,0 +1,19 @@
OBJECTS=$(patsubst %.cpp,%.$(OBJECT_SUFFIX),$(wildcard *.cpp))
.PRECIOUS: %.$(OBJECT_SUFFIX) %.files
__all__.files: $(OBJECTS)
lua ../../genfilelist.lua $^ >$@
echo >__all__.ldflags
%.$(OBJECT_SUFFIX): %.cpp
$(REALCC) -c -o $@ $< -I../../../include -I. $(CFLAGS)
forcelook:
@true
precheck:
@true
clean:
rm -f *.$(OBJECT_SUFFIX) __all__.ldflags __all__.files

View file

@ -0,0 +1,71 @@
#include "demo.hpp"
#include <cstring>
namespace sky
{
uint16_t newdemo_lookup[] = {
0x09, 0x08, 0x0A, 0x08, 0x01, 0x00, 0x02, 0x00, 0x05, 0x04, 0x06, 0x04, 0x01, 0x00, 0x02, 0x00,
0x19, 0x18, 0x1A, 0x18, 0x11, 0x10, 0x12, 0x10, 0x15, 0x14, 0x16, 0x14, 0x11, 0x10, 0x12, 0x10
};
uint16_t skyroads_lookup[] = {
0x009, 0x001, 0x005, 0x201, 0x008, 0x000, 0x004, 0x200,
0x00A, 0x002, 0x006, 0x202, 0x108, 0x100, 0x104, 0x300,
0x019, 0x011, 0x015, 0x211, 0x018, 0x010, 0x014, 0x210,
0x01A, 0x012, 0x016, 0x212, 0x118, 0x110, 0x114, 0x310
};
demo::demo()
{
buffer[0] = 0;
}
demo::demo(const std::vector<char>& demo, bool skyroads_fmt)
{
buffer[0] = skyroads_fmt ? 2 : 1;
uint16_t dptr = 1;
if(buffer[0] == 1) {
//Uncompress RLE.
size_t ptr = 0;
while(ptr < demo.size()) {
uint16_t count = (uint8_t)demo[ptr++];
count++;
uint8_t b = (ptr < demo.size()) ? demo[ptr++] : 5;
for(unsigned i = 0; i < count; i++) {
if(!dptr)
break;
buffer[dptr++] = b;
}
}
}
if(buffer[0] == 2) {
for(unsigned i = 0; i < demo.size(); i++) {
if(!dptr)
break;
buffer[dptr++] = demo[i];
}
}
while(dptr)
buffer[dptr++] = 5; //Neutral input.
}
uint16_t demo::fetchkeys(uint16_t old, uint32_t lpos, uint32_t frame)
{
switch(buffer[0]) {
case 1:
old &= 96;
if(frame < 65535)
old |= newdemo_lookup[buffer[frame + 1] & 31];
break;
case 2:
old &= 96;
if(lpos / 1638 < 65535)
old |= skyroads_lookup[buffer[lpos / 1638 + 1] & 31];
break;
default:
old &= 127;
break;
}
return old;
}
}

View file

@ -0,0 +1,19 @@
#ifndef _skycore__demo__hpp__included__
#define _skycore__demo__hpp__included__
#include <cstdint>
#include <cstdlib>
#include <vector>
namespace sky
{
struct demo
{
demo();
demo(const std::vector<char>& demo, bool skyroads_fmt);
uint16_t fetchkeys(uint16_t old, uint32_t lpos, uint32_t frame);
private:
uint8_t buffer[65536];
};
}
#endif

909
src/emulation/sky/draw.cpp Normal file
View file

@ -0,0 +1,909 @@
#include "draw.hpp"
#include <cmath>
#include "romimage.hpp"
#include "framebuffer.hpp"
#include "messages.hpp"
#include "library/minmax.hpp"
/*
Point of escape is approximately at normalized y=0.16.
Baseline is approximately at normalized y=0.52
1st tile after starts approximately at y=0.38
*/
namespace sky
{
//Projection parameters.
//Inverse of normalized y0 at baseline.
const double z0 = 1.9;
//Depth projection scale.
const double zscale = 0.6;
//Point of escape on screen.
const double yescape = 0.16;
//Horizontal draw scale.
const double hscale = 0.15;
//Vertical draw scale.
const double vscale = 0.1;
//Normalized floor thickness.
const double fthickness = 0.33;
const unsigned pipe_slices = 256;
//Type of point representation.
struct point_t
{
double x;
double y;
};
struct pipe_cache
{
double min_h;
double min_v;
double max_h;
double max_v;
//double dmin;
//double dmax;
//double raise[pipe_slices];
uint32_t colors[pipe_slices];
};
struct pipe_cache pipecache[7];
unsigned top_palette[] = {61, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
unsigned front_palette[] = {62, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
unsigned right_palette[] = {63, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45};
unsigned left_palette[] = {64, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60};
double slopepoints[] = {-3.45, -2.45, -1.45, 0, 1.45, 2.45, 3.45};
double angles[] = {1.35, 1.07, 0.88, 0.745};
const char thruster_pn[80] ="BCAAABACBBABCABBCAACBBBACBCCBBCABACACABCBABCBBACBABCCABACCAAABBCBABCAACBAABBACC";
double calc_slope(double x)
{
return 1.6 * (hscale * x) / (1 / z0 - yescape);
}
std::pair<signed, signed> point_project(double x, double y, double z)
{
double c = (z0 + zscale * z);
if(c < 0.001)
c = 0.001;
double y0 = 1 / c;
double s = (y0 - yescape) / (1 / z0 - yescape);
if(s >= 0)
return std::make_pair(FB_WIDTH * (hscale * x * s + 0.5), FB_HEIGHT * (y0 - vscale * y * s));
else
return std::make_pair(FB_WIDTH / 2, FB_HEIGHT * yescape);
}
point_t point_project_b(double x, double y, double z)
{
double c = (z0 + zscale * z);
if(c < 0.001)
c = 0.001;
double y0 = 1 / c;
double s = (y0 - yescape) / (1 / z0 - yescape);
point_t p;
if(s >= 0) {
p.x = FB_WIDTH * (hscale * x * s + 0.5);
p.y = FB_HEIGHT * (y0 - vscale * y * s);
} else {
p.x = FB_WIDTH / 2;
p.y = FB_HEIGHT * yescape;
}
return p;
}
int32_t ship_sprite_normal(uint16_t hpos, int vdir, uint8_t thruster)
{
int hdir;
if(hpos < 18048) hdir = 0;
else if(hpos < 23936) hdir = 1;
else if(hpos < 29824) hdir = 2;
else if(hpos < 35712) hdir = 3;
else if(hpos < 41600) hdir = 4;
else if(hpos < 47488) hdir = 5;
else hdir = 6;
if(vdir == -1) vdir = 2;
return 9 * hdir + 3 * vdir + thruster + 14;
}
int32_t ship_sprite_explode(int32_t frame)
{
return (frame < 42) ? (frame / 3) : -1;
}
int32_t ship_sprite(physics& p)
{
if(p.expframe)
return ship_sprite_explode(p.expframe);
if(p.death == physics::death_finished)
return -1;
int vdir;
uint16_t thruster;
if(p.vspeed > 300) vdir = 1;
else if(p.vspeed < -300) vdir = -1;
else vdir = 0;
if(p.death == physics::death_fuel)
thruster = 0;
else {
//Some kind of PN sequence.
thruster = thruster_pn[p.framecounter % (sizeof(thruster_pn) - 1)] - 'A';
}
return ship_sprite_normal(p.hpos, vdir, thruster % 3);
}
void draw_sprite(double h, double v, int32_t num)
{
if(num < 0)
return;
signed x = FB_WIDTH * hscale * h + FB_WIDTH / 2 - FB_SCALE * 15;
signed y = FB_HEIGHT / z0 - FB_HEIGHT * vscale * v - FB_SCALE * ship.width + FB_SCALE;
uint32_t offset = ship.width * 30 * num;
//For some darn reason, CARS.LZS has the image rotated 90 degrees counterclockwise.
for(signed j = 0; j < FB_SCALE * (int)ship.width; j++) {
if(y + j < 0 || y + j > overlap_end)
continue;
uint32_t offset2 = offset + j / FB_SCALE;
size_t base = FB_WIDTH * (y + j) + x;
for(signed i = 0; i < 30 * FB_SCALE; i++) {
if(x + i < 0)
continue;
if(x + i >= FB_WIDTH)
break;
uint8_t c = ship.decode[offset2 + ship.width * (i / FB_SCALE)];
if(!c)
continue;
uint32_t pix = ship.palette[c] & 0xFFFFFF;
if((framebuffer[base + i] >> 24) == 0)
framebuffer[base + i] = pix;
}
}
}
void draw_grav_g_meter(gstate& s)
{
uint16_t realgrav = 100 * (s.p.gravity - 3);
uint16_t x = 116;
uint16_t y = 156;
static size_t sep = strlen(_numbers_g) / 10;
while(realgrav) {
uint8_t digit = realgrav % 10;
draw_block2(_numbers_g + sep * digit, y * 320 + (x - 5), dashpalette[6], dashpalette[5],
true);
x -= 5;
realgrav /= 10;
}
}
void draw_indicator(uint8_t& curval, uint8_t newval, gauge& g, uint8_t on1, uint8_t on2, uint8_t off1,
uint8_t off2)
{
unsigned tmp = newval;
if(tmp > g.maxlimit()) tmp = g.maxlimit();
if(curval < tmp)
for(unsigned i = curval; i < tmp; i++) {
draw_block(g.get_data(i), g.get_position(i), dashpalette[on1], dashpalette[on2]);
}
else
for(unsigned i = tmp; i < curval; i++) {
draw_block(g.get_data(i), g.get_position(i), dashpalette[off1], dashpalette[off2]);
}
curval = tmp;
}
void draw_gauges(gstate& s)
{
//draw_grav_g_meter(s);
uint8_t timermod = ((s.p.framecounter % 9) > 4) ? 1 : 0;
draw_indicator(s.speedind, (s.p.lspeed - s.p.speedbias) / 0x141, speed_dat, 2, 3, 0, 1);
draw_indicator(s.o2ind, (s.p.o2_left + 0xbb7) / 0xbb8, oxydisp_dat, 2, 3, 0, 1);
draw_indicator(s.fuelind, (s.p.fuel_left + 0xbb7) / 0xbb8, fueldisp_dat, 2, 3, 0, 1);
//Lock indicator.
bool lck = s.p.is_set(physics::flag_locked);
if(lck != s.lockind)
draw_block2(_lockind_g + (lck ? strlen(_lockind_g) / 2 : 0), 0x9c * 320 + 0xcb,
dashpalette[6], dashpalette[5], true);
s.lockind = lck;
//Out of oxygen blink&beep.
if(s.p.death == physics::death_o2 && s.beep_phase != timermod) {
blink_between(0xa0, 0xa1, 7, 7, dashpalette[7], dashpalette[8]);
if(timermod)
gsfx(sound_beep);
}
//Out of fuel blink&beep.
if(s.p.death == physics::death_fuel && s.beep_phase != timermod) {
blink_between(0x9b, 0xa9, 16, 5, dashpalette[7], dashpalette[8]);
if(timermod)
gsfx(sound_beep);
}
//Distance gauge.
uint32_t res = s.curlevel.apparent_length() / 88;
size_t tmp = (s.p.lpos - 3 * 65536) / res;
for(unsigned i = s.distind; i < tmp && i < 88; i++)
draw_distance_column(i, dashpalette[4]);
s.distind = tmp;
s.beep_phase = timermod;
}
inline double horizon_distance() { return (1 / yescape - z0) / zscale; }
inline double near_distance() { return (600 / overlap_end - z0) / zscale; }
void draw_quad_x(point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color)
{
if(fabs(p1.x - p2.x) < 0.1)
return;
double qp1 = min(p1.x, p2.x);
double qp2 = max(p1.x, p2.x);
int x1 = floor(qp1);
int x2 = ceil(qp2);
double utmax = max(p1.y, p2.y);
double utmin = min(p1.y, p2.y);
double ltmax = max(p3.y, p4.y);
double ltmin = min(p3.y, p4.y);
signed y1 = max((int)floor(utmin), 0);
signed y2 = min((int)ceil(ltmax), (int)overlap_end);
for(signed j = y1; j < y2; j++) {
signed xstart = x1, xend = x2;
if(j < utmax) {
if(p1.y > p2.y)
xstart = floor((qp1 - qp2) * (j - p2.y) / (p1.y - p2.y) + qp2);
else
xend = ceil((qp2 - qp1) * (j - p1.y) / (p2.y - p1.y) + qp1);
}
if(j >= ltmin) {
if(p3.y > p4.y)
xend = ceil((qp1 - qp2) * (j - p4.y) / (p3.y - p4.y) + qp2);
else
xstart = floor((qp2 - qp1) * (j - p3.y) / (p4.y - p3.y) + qp1);
}
xstart = max(xstart, 0);
xend = min(xend, FB_WIDTH);
size_t base = FB_WIDTH * j;
if(j < overlap_start)
for(signed i = xstart; i < xend; i++)
framebuffer[base + i] = color;
else
for(signed i = xstart; i < xend; i++)
framebuffer_blend2(framebuffer[base + i], color);
}
}
void draw_quad_y(point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color)
{
if(fabs(p1.y - p3.y) < 0.1)
return;
signed y1 = max((int)floor(p1.y), 0);
signed y2 = min((int)ceil(p3.y), (int)overlap_end);
for(signed j = y1; j < y2; j++) {
signed xstart, xend;
xstart = floor((p3.x - p1.x) * (j - p1.y) / (p3.y - p1.y) + p1.x);
xend = ceil((p4.x - p2.x) * (j - p1.y) / (p3.y - p1.y) + p2.x);
xstart = max(xstart, 0);
xend = min(xend, FB_WIDTH);
size_t base = FB_WIDTH * j;
if(j < overlap_start)
for(signed i = xstart; i < xend; i++)
framebuffer[base + i] = color;
else
for(signed i = xstart; i < xend; i++)
framebuffer_blend2(framebuffer[base + i], color);
}
}
void draw_quad_z(point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color)
{
if(fabs(p1.x - p2.x) < 0.1 || fabs(p1.y - p3.y) < 0.1)
return;
signed x1 = max((int)floor(p1.x), 0);
signed x2 = min((int)ceil(p2.x), FB_WIDTH);
signed y1 = max((int)floor(p1.y), 0);
signed y2 = min((int)ceil(p3.y), (int)overlap_end);
for(signed j = y1; j < y2; j++) {
size_t base = FB_WIDTH * j;
if(j < overlap_start)
for(signed i = x1; i < x2; i++)
framebuffer[base + i] = color;
else
for(signed i = x1; i < x2; i++)
framebuffer_blend2(framebuffer[base + i], color);
}
}
void draw_quad_z_tunshadow(point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color, int x)
{
if(fabs(p1.x - p2.x) < 0.1 || fabs(p1.y - p3.y) < 0.1)
return;
if(!x)
return; //Center pipe has no shadow.
signed x1 = max((int)floor(p1.x), 0);
signed x2 = min((int)ceil(p2.x), FB_WIDTH);
signed y1 = max((int)floor(p1.y), 0);
signed y2 = min((int)ceil(p3.y), (int)overlap_end);
double slope = calc_slope(slopepoints[x + 3]);
const double width = p2.x - p1.x;
const double hwidth = 0.45 * width;
const double center = (p1.x + p2.x) / 2;
for(signed j = y1; j < y2; j++) {
signed c1 = x2, c2 = x1;
size_t base = FB_WIDTH * j;
double ry = (p3.y - j) / (0.625 * vscale / hscale * 2 * hwidth);
if(ry < 1) {
c1 = center - hwidth * sqrt(1 - ry * ry);
c2 = center + hwidth * sqrt(1 - ry * ry);
}
if(x < 0) {
//The shadow is on left.
c2 = center - hwidth - slope * (p3.y - j);
} else {
//The shadow is on right.
c1 = center + hwidth - slope * (p3.y - j);
}
c1 = max(c1, 0);
c2 = min(c2, FB_WIDTH);
if(j < overlap_start)
for(signed i = c1; i < c2; i++)
framebuffer[base + i] = color;
else
for(signed i = c1; i < c2; i++)
framebuffer_blend2(framebuffer[base + i], color);
}
}
void draw_quad_z_tunnel(point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color)
{
if(fabs(p1.x - p2.x) < 0.1 || fabs(p1.y - p3.y) < 0.1)
return;
signed x1 = max((int)floor(p1.x), 0);
signed x2 = min((int)ceil(p2.x), FB_WIDTH);
signed y1 = max((int)floor(p1.y), 0);
signed y2 = min((int)ceil(p3.y), (int)overlap_end);
const double width = p2.x - p1.x;
const double hwidth = 0.45 * width;
const double center = (p1.x + p2.x) / 2;
for(signed j = y1; j < y2; j++) {
signed c1 = x2, c2 = x2;
size_t base = FB_WIDTH * j;
double ry = (p3.y - j) / (0.625 * vscale / hscale * 2 * hwidth);
if(ry < 1) {
c1 = center - hwidth * sqrt(1 - ry * ry);
c2 = center + hwidth * sqrt(1 - ry * ry);
}
x1 = max(x1, 0);
c2 = max(c2, 0);
x2 = min(x2, FB_WIDTH);
c1 = min(c1, FB_WIDTH);
if(j < overlap_start) {
for(signed i = x1; i < c1; i++)
framebuffer[base + i] = color;
for(signed i = c2; i < x2; i++)
framebuffer[base + i] = color;
} else {
for(signed i = x1; i < c1; i++)
framebuffer_blend2(framebuffer[base + i], color);
for(signed i = c2; i < x2; i++)
framebuffer_blend2(framebuffer[base + i], color);
}
}
}
void draw_quad_z_pipefront(double z, int x, uint32_t color)
{
struct pipe_cache& p = pipecache[x + 3];
auto p5 = point_project_b(x - 0.5, 0, z);
auto p6 = point_project_b(x, 1, z);
auto p7 = point_project_b(x + 0.5, 0, z);
double ymind = p6.y;
double ymaxd = p5.y;
int16_t ymin = floor(ymind);
int16_t ymax = ceil(ymaxd);
if(ymaxd - ymind < 0.1)
return;
int16_t cheight = floor(p5.y - p6.y);
int16_t ciheight = floor(0.9 * (p5.y - p6.y));
int16_t cwidth = floor((p7.x - p5.x) / 2);
int16_t ciwidth = floor(0.9 * (p7.x - p5.x) / 2);
int16_t ccenter = floor((p7.x + p5.x) / 2);
int16_t nxs = ccenter - cwidth;
int16_t nxe = ccenter + cwidth;
for(signed j = ymax - cheight; j < ymax; j++) {
if(j < 0 || j > overlap_end)
continue;
int16_t dstart = nxs;
int16_t dend = nxe;
int16_t cstart = ccenter;
int16_t cend = ccenter;
int16_t cistart = ccenter;
int16_t ciend = ccenter;
if(true) {
int16_t o = ymax - j;
double o2 = 1.0 * o / cheight;
int16_t w = cwidth * sqrt(1 - o2 * o2);
cstart -= w;
cend += w;
}
if(j >= ymax - ciheight) {
int16_t o = ymax - j;
double o2 = 1.0 * o / ciheight;
int16_t w = ciwidth * sqrt(1 - o2 * o2);
cistart -= w;
ciend += w;
}
dstart = max(cstart, (int16_t)0);
dend = min(cend, (int16_t)FB_WIDTH);
size_t base = FB_WIDTH * j;
if(j < overlap_start)
for(signed i = dstart; i < dend; i++) {
if(i < cistart || i >= ciend)
framebuffer[base + i] = color;
}
else
for(signed i = dstart; i < dend; i++) {
if(i < cistart || i >= ciend)
framebuffer_blend2(framebuffer[base + i], color);
}
}
}
// x = (k*y0+b)*c*(0.5*sin(alpha)+X0)
// y = -(k*y0+b)*d*cos(alpha)+y0
//
// dx/dy0 = k*c*0.5*sin(alpha)+k*c*X0
// dy/dy0 = k*d*cos(alpha)+1
// dx/dy = (k*c*0.5*sin(alpha)+k*c*X0)/(k*d*cos(alpha)+1)
// d/dalpha[(k*c*0.5*sin(alpha)+k*c*X0)/(k*d*cos(alpha)+1)]
//=(k*c*0.5*cos(alpha))/(k*d*cos(alpha)+1) +(k*c*0.5*sin(alpha)+k*c*X0)*(k*d*sin(alpha))/(k*d*cos(alpha)+1)^2
//
// 0.5*k*d+0.5*cos(alpha)+X0*(k*d*sin(alpha)) = 0
uint32_t mix_color(uint32_t* c, uint32_t c2)
{
uint32_t low = c[c2 / 256];
uint32_t high = c[c2 / 256 + 1];
uint16_t mod = c2 % 256;
uint16_t imod = 256 - mod;
const uint32_t Lm = 0x00FF00FF;
const uint32_t Hm = 0xFF00FF00;
uint32_t L = (((low & Lm) * imod + (high & Lm) * mod) >> 8) & Lm;
uint32_t H = (((low & Hm) >> 8) * imod + ((high & Hm) >> 8) * mod) & Hm;
return L | H;
}
void rebuild_pipe_quad_cache(pipe_cache& p, int x, uint32_t* c)
{
double k = 1 / (1 / z0 - yescape);
double b = -yescape * k;
double gmin = 999999;
double gmax = -999999;
double amin = 999999;
double amax = -999999;
double y00 = (1-b)/k;
for(unsigned i = 0; i < 256; i++)
p.colors[i] = 0xFFFFFFFF;
//Compute span range.
for(double a = -M_PI / 2; a < M_PI / 2; a += 0.01) {
double A = vscale * cos(a);
double y0 = (y00 + b * A) / (1 - k * A);
double xp = (k * y0 + b) * (x + 0.5 * sin(a));
if(xp < gmin) {
p.min_h = x + 0.5 * sin(a);
p.min_v = cos(a);
gmin = xp;
amin = a;
}
if(xp > gmax) {
p.max_h = x + 0.5 * sin(a);
p.max_v = cos(a);
gmax = xp;
amax = a;
}
}
for(double a = amin; a < amax; a += 0.01) {
double A = vscale * cos(a);
double y0 = (y00 + b * A) / (1 - k * A);
double xp = (k * y0 + b) * (x + 0.5 * sin(a));
signed c2 = (a + M_PI / 2) * 1280 / M_PI;
signed x = 255 * (xp - gmin) / (gmax - gmin);
c2 = max(min(c2, 1280), 0);
x = max(min(x, 255), 0);
p.colors[x] = mix_color(c, c2);
}
for(unsigned i = 0; i < 256; i++) {
if(p.colors[i] == 0xFFFFFFFF)
if(i == 0) {
for(unsigned j = 0; j < 255; j++)
if(p.colors[j] != 0xFFFFFFFF) {
p.colors[i] = p.colors[j];
break;
}
} else
p.colors[i] = p.colors[i - 1];
}
//for(unsigned i = 0; i < 256; i++)
// p.colors[i] = 0xFF8000 + i;
}
void rebuild_pipe_quad_caches(uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4)
{
uint32_t c[6];
c[0] = color4;
c[1] = color3;
c[2] = color2;
c[3] = color1;
c[4] = color2;
c[5] = color3;
for(int x = 0; x < 7; x++)
rebuild_pipe_quad_cache(pipecache[x], x - 3, c);
}
void draw_quad_y_pipe_last(double z, int x, bool top)
{
struct pipe_cache& p = pipecache[x + 3];
uint32_t fcolor = (!top && z < 0.2) ? 0x1000000 : 0;
//We need these for slope projection.
auto p1 = point_project_b(p.min_h, p.min_v, z);
auto p2 = point_project_b(p.max_h, p.max_v, z);
auto p3 = point_project_b(p.min_h, p.min_v, z - 1);
auto p4 = point_project_b(p.max_h, p.max_v, z - 1);
auto p5 = point_project_b(x - 0.5, 0, z);
auto p6 = point_project_b(x, 1, z);
auto p7 = point_project_b(x + 0.5, 0, z);
if(p3.y - p1.y < 0.1)
return;
if(p4.y - p2.y < 0.1)
return;
double ymind = p6.y;
double ymaxd = p5.y;
int16_t ymin = floor(ymind);
int16_t ymax = ceil(ymaxd);
double sl1 = (p3.x - p1.x) / (p3.y - p1.y);
double sl2 = (p4.x - p2.x) / (p4.y - p2.y);
double c1 = p1.x - sl1 * p1.y;
double c2 = p2.x - sl2 * p2.y;
int16_t cheight = floor(p5.y - p6.y);
int16_t cwidth = floor((p7.x - p5.x) / 2);
int16_t ccenter = floor((p7.x + p5.x) / 2);
for(signed j = ymax - cheight; j < ymax; j++) {
if(j < 0 || j > overlap_end)
continue;
int16_t nxs = floor(sl1 * j + c1);
int16_t nxe = ceil(sl2 * j + c2);
int16_t dstart = nxs;
int16_t dend = nxe;
uint32_t cstep = 255 * 65536 / (nxe - nxs + 1);
uint32_t color = 0;
if(true) {
int16_t o = ymax - j;
double o2 = 1.0 * o / cheight;
int16_t w = cwidth * sqrt(1 - o2 * o2);
dstart = ccenter - w;
dend = ccenter + w;
}
dstart = max(max(dstart, nxs), (int16_t)0);
dend = min(min(dend, nxe), (int16_t)FB_WIDTH);
if(dstart > nxs)
color += (dstart - nxs) * cstep;
size_t base = FB_WIDTH * j;
if(j < overlap_start)
for(signed i = dstart; i < dend; i++) {
framebuffer[base + i] = fcolor | p.colors[color >> 16];
color += cstep;
}
else
for(signed i = dstart; i < dend; i++) {
framebuffer_blend2(framebuffer[base + i], fcolor | p.colors[color >> 16]);
color += cstep;
}
}
}
void draw_quad_y_pipe_first(double z1, double z2, int x, bool top)
{
struct pipe_cache& p = pipecache[x + 3];
uint32_t fcolor = (!top && z1 < 0) ? 0x1000000 : 0;
auto p1 = point_project_b(p.min_h, p.min_v, z2);
auto p2 = point_project_b(p.max_h, p.max_v, z2);
auto p3 = point_project_b(p.min_h, p.min_v, z1);
auto p4 = point_project_b(p.max_h, p.max_v, z1);
auto p5 = point_project_b(x - 0.5, 0, z1);
auto p6 = point_project_b(x, 1, z1);
auto p7 = point_project_b(x + 0.5, 0, z1);
double ymind = min(p1.y, p2.y);
double ymaxd = p5.y;
int16_t ymin = floor(ymind);
int16_t ymax = ceil(ymaxd);
int16_t ymin2 = ceil(max(p1.y, p2.y));
if(p3.y - p1.y < 0.1)
return;
if(p4.y - p2.y < 0.1)
return;
double sl1 = (p3.x - p1.x) / (p3.y - p1.y);
double sl2 = (p4.x - p2.x) / (p4.y - p2.y);
double c1 = p1.x - sl1 * p1.y;
double c2 = p2.x - sl2 * p2.y;
int16_t cheight = floor(p5.y - p6.y);
int16_t cwidth = floor((p7.x - p5.x) / 2);
int16_t ccenter = floor((p7.x + p5.x) / 2);
bool hclip = false;
uint16_t mindist = 65535;
for(signed j = ymin; j < ymax; j++) {
if(j < 0 || j > overlap_end)
continue;
int16_t nxs = floor(sl1 * j + c1);
int16_t nxe = ceil(sl2 * j + c2);
int16_t dstart = nxs;
int16_t dend = nxe;
uint32_t cstep = 255 * 65536 / (nxe - nxs + 1);
uint32_t color = 0;
int16_t cstart = ccenter;
int16_t cend = ccenter;
if(j < ymin2 && p1.y != p2.y) {
//The upper triangular region.
if(p1.y < p2.y)
dend = ceil((p2.x - p1.x) * (j - p1.y) / (p2.y - p1.y) + p1.x);
else
dstart = floor((p2.x - p1.x) * (j - p1.y) / (p2.y - p1.y) + p1.x);
}
if(j >= ymax - cheight) {
int16_t o = ymax - j;
double o2 = 1.0 * o / cheight;
int16_t w = cwidth * sqrt(1 - o2 * o2);
cstart -= w;
cend += w;
if(hclip) {
if(x < 0)
dstart = max((int16_t)dstart, cstart);
if(x > 0)
dend = min((int16_t)dend, cend);
} else {
uint16_t dist;
if(x < 0)
dist = cstart - dstart;
if(x > 0)
dist = dend - cend;
if(dist > mindist)
hclip = true;
else
mindist = dist;
}
}
dstart = max(max(dstart, nxs), (int16_t)0);
dend = min(min(dend, nxe), (int16_t)FB_WIDTH);
if(dstart > nxs)
color += (dstart - nxs) * cstep;
size_t base = FB_WIDTH * j;
if(j < overlap_start)
for(signed i = dstart; i < dend; i++) {
if(i < cstart || i >= cend)
framebuffer[base + i] = fcolor | p.colors[color >> 16];
color += cstep;
}
else
for(signed i = dstart; i < dend; i++) {
if(i < cstart || i >= cend)
framebuffer_blend2(framebuffer[base + i], fcolor |
p.colors[color >> 16]);
color += cstep;
}
}
}
void draw_quad_y_pipe(double z1, double z2, int x, bool top)
{
struct pipe_cache& p = pipecache[x + 3];
uint32_t fcolor = (!top && z1 < 0.2) ? 0x1000000 : 0;
auto p1 = point_project_b(p.min_h, p.min_v, z2);
auto p2 = point_project_b(p.max_h, p.max_v, z2);
auto p3 = point_project_b(p.min_h, p.min_v, z1);
auto p4 = point_project_b(p.max_h, p.max_v, z1);
double ymind = min(p1.y, p2.y);
double ymaxd = max(p3.y, p4.y);
int16_t ymin = floor(ymind);
int16_t ymax = ceil(ymaxd) + 1;
int16_t ymin2 = ceil(max(p1.y, p2.y));
int16_t ymax2 = floor(min(p3.y, p4.y));
if(p3.y - p1.y < 0.1)
return;
if(p4.y - p2.y < 0.1)
return;
double sl1 = (p3.x - p1.x) / (p3.y - p1.y);
double sl2 = (p4.x - p2.x) / (p4.y - p2.y);
double c1 = p1.x - sl1 * p1.y;
double c2 = p2.x - sl2 * p2.y;
//FIXME: This leaves some graphical glitching in seams.
for(signed j = ymin; j < ymax; j++) {
if(j < 0 || j > overlap_end)
continue;
int16_t nxs = floor(sl1 * j + c1);
int16_t nxe = ceil(sl2 * j + c2);
int16_t dstart = nxs;
int16_t dend = nxe;
uint32_t cstep = 255 * 65536 / (nxe - nxs + 1);
uint32_t color = 0;
if(j < ymin2 && p1.y != p2.y) {
//The upper triangular region.
if(p1.y < p2.y)
dend = ceil((p2.x - p1.x) * (j - p1.y) / (p2.y - p1.y) + p1.x);
else
dstart = floor((p2.x - p1.x) * (j - p1.y) / (p2.y - p1.y) + p1.x);
}
if(j >= ymax2 && p3.y != p4.y) {
//The lower triangular region.
if(p3.y < p4.y)
dstart = floor((p4.x - p3.x) * (j - p3.y - 1) / (p4.y - p3.y) + p3.x);
else
dend = ceil((p4.x - p3.x) * (j - p3.y - 1) / (p4.y - p3.y) + p3.x);
}
dstart = max(max(dstart, nxs), (int16_t)0);
dend = min(min(dend, nxe), (int16_t)FB_WIDTH);
if(dstart > nxs)
color += (dstart - nxs) * cstep;
size_t base = FB_WIDTH * j;
if(j < overlap_start)
for(signed i = dstart; i < dend; i++) {
framebuffer[base + i] = fcolor | p.colors[color >> 16];
color += cstep;
}
else
for(signed i = dstart; i < dend; i++) {
framebuffer_blend2(framebuffer[base + i], fcolor | p.colors[color >> 16]);
color += cstep;
}
}
}
void draw_level(gstate& s)
{
for(unsigned i = 0; i < overlap_start; i++)
for(unsigned j = 0; j < FB_WIDTH; j++)
framebuffer[i * FB_WIDTH + j] = origbuffer[(i / FB_SCALE) * 320 + (j / FB_SCALE)];
for(unsigned i = overlap_start; i < overlap_end; i++)
for(unsigned j = 0; j < FB_WIDTH; j++)
framebuffer_blend2(framebuffer[i * FB_WIDTH + j], origbuffer[(i / FB_SCALE) * 320 +
(j / FB_SCALE)]);
static signed dorder[] = {-3, 3, -2, 2, -1, 1, 0};
level& l = s.curlevel;
double zship = s.p.lpos / 65536.0;
double horizon = horizon_distance();
double near = near_distance();
signed maxtile = ceil(zship + horizon);
signed mintile = floor(zship + near - 1);
for(signed zt = maxtile; zt >= mintile; zt--) {
for(signed dxt = 0; dxt < 7; dxt++) {
signed xt = dorder[dxt];
tile t = l.at_tile(zt, xt);
tile tleft = l.at_tile(zt, xt - 1);
tile tright = l.at_tile(zt, xt + 1);
tile tfront = l.at_tile(zt - 1, xt);
tile tback = l.at_tile(zt + 1, xt);
//First, draw the floor level.
if(t.lower_floor() && (!t.is_rblock() || t.is_tunnel())) {
bool ifront = (s.p.vpos < 10240);
ifront &= (xt >= 0 || s.p.hpos < 5888 * xt + 32768 - 2944);
ifront &= (xt <= 0 || s.p.hpos > 5888 * xt + 32768 + 2944);
draw_quad_y(point_project_b(xt - 0.5, 0, zt + 1 - zship),
point_project_b(xt + 0.5, 0, zt + 1 - zship),
point_project_b(xt - 0.5, 0, zt - zship),
point_project_b(xt + 0.5, 0, zt - zship),
l.get_palette_color(top_palette[t.lower_floor()], ifront));
}
//Draw pipe shadow.
if(t.is_tunnel() && !tfront.is_block()) {
//Pipe shadow never obscures the ship.
draw_quad_z_tunshadow(point_project_b(xt - 0.5, 1, zt - zship),
point_project_b(xt + 0.5, 1, zt - zship),
point_project_b(xt - 0.5, 0, zt - zship),
point_project_b(xt + 0.5, 0, zt - zship),
l.get_palette_color(t.is_rblock() ? 65 : 67), xt);
}
//Draw the top surface.
if(t.is_rblock()) {
signed q = t.apparent_height();
bool ifront = (s.p.vpos < 10240 + q * 2560);
ifront &= (xt >= 0 || s.p.hpos < 5888 * xt + 32768 - 2944);
ifront &= (xt <= 0 || s.p.hpos > 5888 * xt + 32768 + 2944);
ifront |= (l.in_pipe(zt * 65536, s.p.hpos, s.p.vpos) && zt < zship);
draw_quad_y(point_project_b(xt - 0.5, q, zt + 1 - zship),
point_project_b(xt + 0.5, q, zt + 1 - zship),
point_project_b(xt - 0.5, q, zt - zship),
point_project_b(xt + 0.5, q, zt - zship),
l.get_palette_color(top_palette[t.upper_floor()], ifront));
}
//Left/Right block surface.
if(t.is_rblock() && xt < 0 && tright.apparent_height() < t.apparent_height()) {
signed q1 = tright.apparent_height();
signed q2 = t.apparent_height();
bool ifront = (s.p.vpos < 10240 + q2 * 2560);
ifront &= (s.p.hpos < 5888 * xt + 32768 + 2944);
draw_quad_x(point_project_b(xt + 0.5, q2, zt - zship),
point_project_b(xt + 0.5, q2, zt + 1 - zship),
point_project_b(xt + 0.5, q1, zt - zship),
point_project_b(xt + 0.5, q1, zt + 1 - zship),
l.get_palette_color(63, ifront));
}
if(t.is_rblock() && xt > 0 && tleft.apparent_height() < t.apparent_height()) {
signed q1 = tleft.apparent_height();
signed q2 = t.apparent_height();
bool ifront = (s.p.vpos < 10240 + q2 * 2560);
ifront &= (s.p.hpos > 5888 * xt + 32768 - 2944);
draw_quad_x(point_project_b(xt - 0.5, q2, zt + 1 - zship),
point_project_b(xt - 0.5, q2, zt - zship),
point_project_b(xt - 0.5, q1, zt + 1- zship),
point_project_b(xt - 0.5, q1, zt - zship),
l.get_palette_color(64, ifront));
}
//Front block surface.
if(t.is_rblock() && tfront.apparent_height() < t.apparent_height()) {
signed q1 = tfront.apparent_height();
signed q2 = t.apparent_height();
bool ifront = (zt < zship);
if(t.is_tunnel() && !tfront.is_block())
draw_quad_z_tunnel(point_project_b(xt - 0.5, q2, zt - zship),
point_project_b(xt + 0.5, q2, zt - zship),
point_project_b(xt - 0.5, q1, zt - zship),
point_project_b(xt + 0.5, q1, zt - zship),
l.get_palette_color(62, ifront));
else {
draw_quad_z(point_project_b(xt - 0.5, q2, zt - zship),
point_project_b(xt + 0.5, q2, zt - zship),
point_project_b(xt - 0.5, q1, zt - zship),
point_project_b(xt + 0.5, q1, zt - zship),
l.get_palette_color(62, ifront));
}
}
//Last tunnel block.
if(t.is_tunnel() && !t.is_rblock()) {
bool top = (s.p.vpos > 10240);
top &= !l.in_pipe(zt * 65536, s.p.hpos, s.p.vpos);
if(tback.is_rblock() || !tback.is_block())
draw_quad_y_pipe_last(zt + 1 - zship, xt, top);
//Standalone tunnel top.
if(tfront.is_block())
draw_quad_y_pipe(zt - zship, zt + 1 - zship, xt, top);
else
draw_quad_y_pipe_first(zt - zship, zt + 1 - zship, xt, top);
//Standalone tunnel front.
if(!tfront.is_block()) {
bool ifront = (zt < zship);
draw_quad_z_pipefront(zt - zship, xt, l.get_palette_color(66, ifront));
}
}
//Left/Right floor surface.
if(xt < 0 && t.lower_floor() && !tright.lower_floor()) {
bool ifront = (s.p.vpos < 10240);
ifront &= (s.p.hpos > 5888 * xt + 32768);
draw_quad_x(point_project_b(xt + 0.5, 0, zt - zship),
point_project_b(xt + 0.5, 0, zt + 1 - zship),
point_project_b(xt + 0.5, -fthickness, zt - zship),
point_project_b(xt + 0.5, -fthickness, zt + 1 - zship),
l.get_palette_color(right_palette[t.lower_floor()]));
}
if(xt > 0 && t.lower_floor() && !tleft.lower_floor()) {
bool ifront = (s.p.vpos < 10240);
ifront &= (s.p.hpos > 5888 * xt + 32768);
draw_quad_x(point_project_b(xt - 0.5, 0, zt + 1 - zship),
point_project_b(xt - 0.5, 0, zt - zship),
point_project_b(xt - 0.5, -fthickness, zt + 1- zship),
point_project_b(xt - 0.5, -fthickness, zt - zship),
l.get_palette_color(left_palette[t.lower_floor()], ifront));
}
//Front floor surface.
if(t.lower_floor() && !tfront.lower_floor()) {
bool ifront = (s.p.vpos < 10240);
ifront &= (s.p.hpos < 5888 * xt + 32768);
draw_quad_z(point_project_b(xt - 0.5, 0, zt - zship),
point_project_b(xt + 0.5, 0, zt - zship),
point_project_b(xt - 0.5, -fthickness, zt - zship),
point_project_b(xt + 0.5, -fthickness, zt - zship),
l.get_palette_color(front_palette[t.lower_floor()], ifront));
}
}
}
draw_sprite((s.p.hpos - 32768.0) / 5888.0, (s.p.vpos - 10240.0) / 2560.0,
ship_sprite(s.p));
}
}

View file

@ -0,0 +1,13 @@
#ifndef _skycore__draw__hpp__included__
#define _skycore__draw__hpp__included__
#include "state.hpp"
namespace sky
{
void draw_grav_g_meter(gstate& s);
void draw_gauges(gstate& s);
void draw_level(gstate& s);
void rebuild_pipe_quad_caches(uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4);
}
#endif

View file

@ -0,0 +1,180 @@
#include "framebuffer.hpp"
#include <cstring>
#include <iostream>
namespace sky
{
uint32_t origbuffer[65536];
uint32_t framebuffer[FB_WIDTH * FB_HEIGHT];
uint16_t overlap_start;
uint16_t overlap_end;
void render_backbuffer()
{
for(unsigned i = 0; i < 200; i++) {
for(unsigned j = 0; j < 320; j++) {
for(unsigned k = 0; k < FB_SCALE; k++)
framebuffer[FB_MAJSTRIDE * i + FB_SCALE * j + k] = origbuffer[320 * i + j];
}
for(unsigned k = 1; k < FB_SCALE; k++)
memcpy(framebuffer + (FB_MAJSTRIDE * i + k * FB_WIDTH),
framebuffer + FB_MAJSTRIDE * i, FB_WIDTH * sizeof(uint32_t));
}
//Calculate overlap region.
for(unsigned i = 0; i < 200; i++) {
uint32_t high = 0x00000000U;
for(unsigned j = 0; j < 320; j++)
high |= origbuffer[320 * i + j];
if(high & 0xFF000000U) {
overlap_start = FB_SCALE * i;
break;
}
}
for(unsigned i = overlap_start / FB_SCALE; i < 200; i++) {
uint32_t low = 0xFFFFFFFFU;
for(unsigned j = 0; j < 320; j++)
low &= origbuffer[320 * i + j];
if((low & 0xFF000000U) == 0xFF000000U) {
overlap_end = FB_SCALE * i;
break;
}
}
}
void render_framebuffer_update(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
if(x >= 320)
return;
for(unsigned i = y; i < y + h && i < 200; i++) {
for(unsigned j = x; j < x + w && j < 320; j++) {
for(unsigned k = 0; k < FB_SCALE; k++)
framebuffer[FB_MAJSTRIDE * i + FB_SCALE * j + k] = origbuffer[320 * i + j];
}
for(unsigned k = 1; k < FB_SCALE; k++)
memcpy(framebuffer + (FB_MAJSTRIDE * i + FB_SCALE * x + k * FB_WIDTH), framebuffer +
FB_MAJSTRIDE * i + FB_SCALE * x, FB_SCALE * w * sizeof(uint32_t));
}
}
void render_framebuffer_vline(uint16_t x, uint16_t y1, uint16_t y2, uint32_t color)
{
for(unsigned i = y1; i <= y2 && i < 200; i++) {
for(unsigned k = 0; k < FB_SCALE; k++)
framebuffer[FB_MAJSTRIDE * i + x + k * FB_WIDTH] = 0xFF000000U | color;
}
}
char decode(const char* ch)
{
char c = *ch;
if(c >= '\\')
c--;
return c - 35;
}
void draw_block2(const char* pointer, uint16_t position, uint32_t c1, uint32_t c2, bool zero)
{
static uint8_t tbl[3][6] = {{27, 9, 3, 1}, {32, 16, 8, 4, 2, 1}, {32, 16, 8, 4, 2, 1}};
static uint8_t tbl2[3][3] = {{4, 6, 6}, {3, 2, 2}, {0, 0, 1}};
c1 |= 0xFF000000U;
c2 |= 0xFF000000U;
uint8_t type = decode(pointer++);
uint16_t width = decode(pointer++);
if(width > 63) width = (width - 64) * 64 + decode(pointer++);
uint16_t height = decode(pointer++);
if(height > 63) height = (height - 64) * 64 + decode(pointer++);
uint16_t origptr = position;
uint8_t mod = 0;
uint8_t reg = 0;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
if(!mod)
reg = decode(pointer++);
uint8_t p = ((reg / tbl[type][mod]) % tbl2[1][type]) << tbl2[2][type];
mod = (mod + 1) % tbl2[0][type];
if(p)
origbuffer[position] = (p > 1) ? c2 : c1;
else if(zero)
origbuffer[position] = 0;
position++;
}
position += (320 - width);
}
render_framebuffer_update(origptr % 320, origptr / 320, width, height);
}
void draw_block(const uint8_t* pointer, uint16_t position, uint32_t c1, uint32_t c2, bool zero)
{
c1 |= 0xFF000000U;
c2 |= 0xFF000000U;
uint16_t width = *(pointer++);
uint16_t height = *(pointer++);
uint16_t origptr = position;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
uint8_t p = *(pointer++);
if(p)
origbuffer[position] = (p > 1) ? c2 : c1;
else if(zero)
origbuffer[position] = 0;
position++;
}
position += (320 - width);
}
render_framebuffer_update(origptr % 320, origptr / 320, width, height);
}
void draw_message(const char* pointer, uint32_t c1, uint32_t c2)
{
auto op = pointer;
pointer++;
uint16_t width = decode(pointer++);
if(width > 63) width = (width - 64) * 64 + decode(pointer++);
uint16_t height = decode(pointer++);
if(height > 63) height = (height - 64) * 64 + decode(pointer++);
uint16_t x = (320 - width) / 2;
uint16_t y = (200 - height) / 2;
uint16_t p = 320 * y + x;
draw_block2(op, p, c1, c2);
}
void draw_distance_column(uint16_t col, uint32_t c)
{
uint16_t minline = 0x8f;
uint16_t maxline = 0x8f;
uint16_t ptr = 320 * 0x8f + 0x2a + (col / 3);
uint32_t px = origbuffer[ptr] ;
while(origbuffer[ptr] == px)
ptr -= 320;
ptr += 320;
minline = ptr / 320;
while(origbuffer[ptr] == px)
ptr += 320;
maxline = ptr / 320 - 1;
render_framebuffer_vline(col + 3 * 0x2a, minline, maxline, c | 0xFF000000U);
}
void blink_between(unsigned x, unsigned y, unsigned w, unsigned h, uint32_t c1, uint32_t c2)
{
c1 &= 0xFFFFFF;
c2 &= 0xFFFFFF;
uint32_t c1x = c1 | 0xFF000000U;
uint32_t c2x = c2 | 0xFF000000U;
for(unsigned j = y; j < y + h; j++)
for(unsigned i = x; i < x + w; i++) {
if((origbuffer[320 * j + i] & 0xFFFFFF) == c1)
origbuffer[320 * j + i] = c2x;
else if((origbuffer[320 * j + i] & 0xFFFFFF) == c2)
origbuffer[320 * j + i] = c1x;
}
render_framebuffer_update(x, y, w, h);
}
void draw_bitmap(const uint32_t* bitmap, unsigned x, unsigned y)
{
for(unsigned j = 0; j < bitmap[1]; j++)
for(unsigned i = 0; i < bitmap[0]; i++)
framebuffer[(y + j) * FB_WIDTH + (x + i)] = bitmap[j * bitmap[0] + i + 2];
}
}

View file

@ -0,0 +1,48 @@
#ifndef _skycore__framebuffer__hpp__included__
#define _skycore__framebuffer__hpp__included__
#include <cstdint>
#define FB_SCALE 3
#define FB_WIDTH (FB_SCALE * 320)
#define FB_HEIGHT (FB_SCALE * 200)
#define FB_MAJSTRIDE (FB_SCALE * FB_SCALE * 320)
namespace sky
{
extern uint32_t origbuffer[65536];
extern uint32_t framebuffer[FB_WIDTH * FB_HEIGHT];
extern uint16_t overlap_start;
extern uint16_t overlap_end;
inline void framebuffer_blend1(uint32_t& fbpix, uint32_t rpixel)
{
fbpix = rpixel & 0x00FFFFFFU;
}
inline void framebuffer_blend2(uint32_t& fbpix, uint32_t rpixel)
{
if((fbpix >> 31) == 0)
fbpix = rpixel & 0x01FFFFFFU;
}
//Take origbuffer and completely rerender framebuffer.
void render_backbuffer();
//Take a reigon of origbuffer and rerender that into framebuffer.
void render_framebuffer_update(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
//Render a vertical line into framebuffer. Uses n*320x200 screen size!
void render_framebuffer_vline(uint16_t x, uint16_t y1, uint16_t y2, uint32_t color);
//Render a gauge block.
void draw_block(const uint8_t* pointer, uint16_t position, uint32_t c1, uint32_t c2, bool zero = false);
void draw_block2(const char* pointer, uint16_t position, uint32_t c1, uint32_t c2, bool zero = false);
//Render a message.
void draw_message(const char* pointer, uint32_t c1, uint32_t c2);
//Draw column in distance gauge.
void draw_distance_column(uint16_t col, uint32_t c);
//Blink between colors in specified block.
void blink_between(unsigned x, unsigned y, unsigned w, unsigned h, uint32_t c1, uint32_t c2);
//Draw a bitmap at full resolution.
void draw_bitmap(const uint32_t* bitmap, unsigned x, unsigned y);
}
#endif

View file

@ -0,0 +1,36 @@
#include "gauge.hpp"
#include "util.hpp"
#include "library/string.hpp"
namespace sky
{
gauge::gauge()
{
dummyimage[0] = dummyimage[1] = 0;
}
gauge::gauge(const std::vector<char>& dispdat, size_t cells)
{
dummyimage[0] = dummyimage[1] = 0;
if(dispdat.size() < 2 * cells)
(stringfmt() << "Entry pointer table incomplete").throwex();
data.resize(dispdat.size() - 2 * cells);
memcpy(&data[0], &dispdat[2 * cells], data.size());
for(unsigned i = 0; i < cells; i++)
unpack_image(dispdat, i, cells);
}
void gauge::unpack_image(const std::vector<char>& dispdat, size_t i, size_t total)
{
size_t offset = combine(dispdat[2 * i + 0], dispdat[2 * i + 1]);
//Check that the offset is in-range.
if(data.size() < offset)
(stringfmt() << "Entry " << i << " points outside file").throwex();
if(data.size() < offset + 4)
(stringfmt() << "Entry " << i << " header incomplete").throwex();
size_t imagesize = (uint16_t)data[offset + 2] * data[offset + 3];
if(data.size() < offset + 4 + imagesize)
(stringfmt() << "Entry " << i << " bitmap incomplete").throwex();
ptr.push_back(offset);
}
}

View file

@ -0,0 +1,29 @@
#ifndef _skycore__gauge__hpp__included__
#define _skycore__gauge__hpp__included__
#include <cstdint>
#include <iostream>
#include <cstring>
#include <vector>
#include "util.hpp"
namespace sky
{
struct gauge
{
gauge();
gauge(const std::vector<char>& dispdat, size_t cells);
uint16_t get_position(size_t idx)
{
return (idx < ptr.size()) ? combine(data[ptr[idx]], data[ptr[idx] + 1]) : 0;
}
uint8_t* get_data(size_t idx) { return (idx < ptr.size()) ? &data[ptr[idx]] + 2 : dummyimage; }
size_t maxlimit() { return ptr.size(); }
private:
void unpack_image(const std::vector<char>& dispdat, size_t sequence, size_t total);
std::vector<uint8_t> data;
std::vector<size_t> ptr;
uint8_t dummyimage[2];
};
}
#endif

View file

@ -0,0 +1,70 @@
#include "image.hpp"
#include "util.hpp"
namespace sky
{
struct vector_output_stream : public output_stream
{
vector_output_stream(std::vector<uint8_t>& _data);
~vector_output_stream();
void put(uint8_t byte);
private:
std::vector<uint8_t>& data;
};
vector_output_stream::vector_output_stream(std::vector<uint8_t>& _data)
: data(_data)
{
}
vector_output_stream::~vector_output_stream()
{
}
void vector_output_stream::put(uint8_t byte)
{
data.push_back(byte);
}
image::image()
{
memset(palette, 0, 256 * sizeof(uint32_t));
memset(unknown2, 0, 512);
unknown1 = 0;
width = 0;
height = 0;
}
image::image(const std::vector<char>& data)
{
memset(palette, 0, 256 * sizeof(uint32_t));
memset(unknown2, 0, 512);
if(data.size() < 5)
throw std::runtime_error("Expected CMAP magic, got EOF");
if(data[0] != 'C' || data[1] != 'M' || data[2] != 'A' || data[3] != 'P')
throw std::runtime_error("Bad CMAP magic");
colors = data[4];
if(data.size() < 5 * colors + 5)
throw std::runtime_error("Expected CMAP, got EOF");
for(unsigned i = 0; i < colors; i++) {
palette[i] = ((uint32_t)expand_color(data[3 * i + 5]) << 16) |
((uint32_t)expand_color(data[3 * i + 6]) << 8) |
((uint32_t)expand_color(data[3 * i + 7]));
unknown2[2 * i + 0] = data[3 * colors + 2 * i + 5];
unknown2[2 * i + 1] = data[3 * colors + 2 * i + 6];
}
if(data.size() < 5 * colors + 9)
throw std::runtime_error("Bad PICT magic");
size_t x = 5 * colors + 5;
if(data[x + 0] != 'P' || data[x + 1] != 'I' || data[x + 2] != 'C' || data[x + 3] != 'T')
throw std::runtime_error("Bad PICT magic");
if(data.size() < 5 * colors + 15)
throw std::runtime_error("Expected PICT header, got EOF");
unknown1 = combine(data[x + 4], data[x + 5]);
height = combine(data[x + 6], data[x + 7]);
width = combine(data[x + 8], data[x + 9]);
vector_input_stream in(data, x + 10);
vector_output_stream out(decode);
lzs_decompress(in, out, (uint32_t)width * height);
}
}

View file

@ -0,0 +1,27 @@
#ifndef _skycore__image__hpp__included__
#define _skycore__image__hpp__included__
#include <cstdint>
#include <iostream>
#include <cstring>
#include <stdexcept>
#include <vector>
namespace sky
{
struct image
{
image();
image(const std::vector<char>& data);
uint16_t width;
uint16_t height;
uint8_t colors;
uint16_t unknown1;
std::vector<uint8_t> decode;
uint32_t operator[](size_t ptr) { return palette[decode[ptr]]; }
uint32_t palette[256];
uint8_t unknown2[512];
};
}
#endif

266
src/emulation/sky/level.cpp Normal file
View file

@ -0,0 +1,266 @@
#include "level.hpp"
#include "util.hpp"
#include "lzs.hpp"
#include "physics.hpp"
#include "library/string.hpp"
#include "library/sha256.hpp"
#include "library/bintohex.hpp"
#include "library/zip.hpp"
namespace sky
{
struct tile_output_stream : public output_stream
{
tile_output_stream(std::vector<tile>& _out)
: out(_out)
{
polarity = false;
}
void put(unsigned char byte)
{
polarity = !polarity;
if(polarity)
buffered = byte;
else
out.push_back(tile(combine(buffered, byte)));
}
private:
std::vector<tile>& out;
uint8_t buffered;
bool polarity;
};
level::level()
{
memset(this, 0, sizeof(*this));
length = 0;
gravity = 8;
o2_amount = 1;
fuel_amount = 1;
}
level::level(rom_level& data)
{
length = data.get_length();
gravity = data.get_gravity();
o2_amount = data.get_o2_amount();
fuel_amount = data.get_fuel_amount();
for(unsigned i = 0; i < 72; i++)
palette[i] = data.get_palette_color(i);
for(unsigned i = 0; i < sizeof(tiles) / sizeof(tiles[0]); i++)
tiles[i] = data.get_tile(i).rawtile();
}
rom_level::rom_level(const std::vector<char>& data, size_t offset, size_t length)
{
gravity = combine(access_array(data, offset + 0), access_array(data, offset + 1));
fuel_amount = combine(access_array(data, offset + 2), access_array(data, offset + 3));
o2_amount = combine(access_array(data, offset + 4), access_array(data, offset + 5));
//Next 216 bytes are the palette table.
for(unsigned i = 0; i < 72; i++) {
uint8_t r = expand_color(access_array(data, offset + 3 * i + 6));
uint8_t g = expand_color(access_array(data, offset + 3 * i + 7));
uint8_t b = expand_color(access_array(data, offset + 3 * i + 8));
palette[i] = ((uint32_t)r << 16) | ((uint32_t)g << 8) | ((uint32_t)b);
}
//After 222 bytes, there is compressed data.
vector_input_stream in(data, offset + 222);
tile_output_stream out(tiles);
lzs_decompress(in, out, length >> 1 << 1);
}
tile level::at(uint32_t lpos, uint16_t hpos)
{
if(hpos < 12160 || hpos >= 53376)
return tile();
else {
size_t ptr = 7 * static_cast<size_t>(lpos >> 16) + (hpos - 12160) / 5888;
ptr = ptr * 2 + (reinterpret_cast<uint8_t*>(tiles) - reinterpret_cast<uint8_t*>(this));
ptr &= 0xFFFF;
return tile(*reinterpret_cast<uint16_t*>(reinterpret_cast<uint8_t*>(this) + ptr));
}
}
tile level::at_tile(int32_t l, int16_t h)
{
if(h < -3 || h > 3)
return tile();
else {
size_t ptr = 7 * static_cast<size_t>(l) + h + 3;
ptr = ptr * 2 + (reinterpret_cast<uint8_t*>(tiles) - reinterpret_cast<uint8_t*>(this));
ptr &= 0xFFFF;
return tile(*reinterpret_cast<uint16_t*>(reinterpret_cast<uint8_t*>(this) + ptr));
}
}
bool level::collides(uint32_t lpos, uint16_t hpos, int16_t vpos)
{
bool debug = (lpos == 1248904);
uint16_t ltile = lpos / 65536;
tile a = at(lpos, hpos - 1792);
tile b = at(lpos, hpos + 1792);
//Floor collision check.
if(a.is_colliding_floor(0, vpos) || b.is_colliding_floor(0, vpos))
return true;
if(a.is_block() || b.is_block()) {
tile c = at(lpos, hpos);
int hchunk = 23 - (hpos / 128 - 49) % 46;
int16_t offset = -5888;
if(hchunk < 0) {
hchunk = 1 - hchunk;
offset = 5888;
}
if(c.is_colliding(hchunk, vpos))
return true;
tile d = at(lpos, hpos + offset);
if(d.is_colliding(47 - hchunk, vpos))
return true;
}
return false;
}
bool level::in_pipe(uint32_t lpos, uint16_t hpos, int16_t vpos)
{
int hchunk = 23 - (hpos / 128 - 49) % 46;
if(hchunk <= 0)
hchunk = 1 - hchunk;
return at(lpos, hpos).in_pipe(hchunk, vpos);
}
inline void hash_uint16(sha256& h, uint16_t v)
{
uint8_t buf[2];
buf[0] = v;
buf[1] = v >> 8;
h.write(buf, 2);
}
void rom_level::sha256_hash(uint8_t* buffer)
{
sha256 h;
hash_uint16(h, gravity);
hash_uint16(h, fuel_amount);
hash_uint16(h, o2_amount);
for(auto i : tiles)
hash_uint16(h, i.rawtile());
h.read(buffer);
}
roads_lzs::roads_lzs()
{
for(size_t i = 0; i < 31; i++)
l[i] = NULL;
}
roads_lzs::roads_lzs(const std::vector<char>& file)
{
for(size_t i = 0; i < 31; i++)
l[i] = NULL;
for(size_t i = 0; i < 31; i++) {
try {
uint16_t off = combine(access_array(file, 4 * i + 0), access_array(file, 4 * i + 1));
uint16_t len = combine(access_array(file, 4 * i + 2), access_array(file, 4 * i + 3));
if(len)
l[i] = new rom_level(file, off, len);
} catch(...) {
for(size_t i = 0; i < 31; i++)
if(l[i] != NULL)
delete l[i];
throw;
}
}
}
roads_lzs::~roads_lzs()
{
for(size_t i = 0; i < 31; i++)
if(l[i] != NULL)
delete l[i];
}
roads_lzs::roads_lzs(const roads_lzs& x)
{
for(size_t i = 0; i < 31; i++)
l[i] = NULL;
try {
for(size_t i = 0; i < 31; i++)
l[i] = (x.l[i] != NULL) ? new rom_level(*x.l[i]) : NULL;
} catch(...) {
for(size_t i = 0; i < 31; i++)
if(l[i] != NULL)
delete l[i];
throw;
}
}
roads_lzs& roads_lzs::operator=(const roads_lzs& x)
{
if(this == &x)
return *this;
rom_level* tmp[31];
for(size_t i = 0; i < 31; i++)
tmp[i] = NULL;
try {
for(size_t i = 0; i < 31; i++)
tmp[i] = (x.l[i] != NULL) ? new rom_level(*x.l[i]) : NULL;
} catch(...) {
for(size_t i = 0; i < 31; i++)
if(tmp[i] != NULL)
delete tmp[i];
throw;
}
for(size_t i = 0; i < 31; i++) {
delete l[i];
l[i] = tmp[i];
}
return *this;
}
}
#ifdef LEVEL_CHECKSUMMER
int main(int argc, char** argv)
{
sky::roads_lzs levels(read_file_relative(argv[1], ""));
uint32_t democount = 0;
for(unsigned i = 0; i < 31; i++) {
if(levels.present(i)) {
uint8_t x[32];
levels[i].sha256_hash(x);
std::string demofile = (stringfmt() << argv[2] << i).str();
try {
std::vector<char> dem = read_file_relative(demofile, "");
if(dem.size() == 0)
continue;
char d = 1;
std::cout.write(&d, 1);
std::cout.write((char*)x, 32);
std::vector<char> comp;
size_t dpos = 0;
size_t run_start = 0;
size_t run_byte = dem[0];
while(dpos < dem.size()) {
if(dpos - run_start > 256 || dem[dpos] != run_byte) {
comp.push_back(dpos - run_start - 1);
comp.push_back(run_byte);
run_start = dpos;
run_byte = dem[dpos];
}
dpos++;
}
comp.push_back(dpos - run_start - 1);
comp.push_back(run_byte);
char cmplen[3];
cmplen[0] = comp.size() >> 16;
cmplen[1] = comp.size() >> 8;
cmplen[2] = comp.size();
std::cout.write(cmplen, 3);
std::cout.write(&comp[0], comp.size());
democount++;
std::cerr << "Demo entry for level " << i << ": " << dem.size() << " -> "
<< comp.size() << std::endl;
} catch(...) {
}
}
}
std::cerr << "Wrote " << democount << " demo entries." << std::endl;
}
#endif

View file

@ -0,0 +1,72 @@
#ifndef _skycore__level__hpp__included__
#define _skycore__level__hpp__included__
#include <cstdint>
#include <iostream>
#include <cstring>
#include <vector>
#include "tile.hpp"
namespace sky
{
struct rom_level;
struct level
{
level();
level(rom_level& data);
int16_t get_gravity() { return gravity; }
int16_t get_o2_amount() { return o2_amount; }
uint16_t get_fuel_amount() { return fuel_amount; }
uint32_t get_palette_color(size_t idx, bool setC24 = false)
{
return (setC24 ? 0x1000000 : 0) | ((idx < 72) ? palette[idx] : 0);
}
tile at(uint32_t lpos, uint16_t hpos);
tile at_tile(int32_t l, int16_t h);
bool collides(uint32_t lpos, uint16_t hpos, int16_t vpos);
bool in_pipe(uint32_t lpos, uint16_t hpos, int16_t vpos);
uint32_t finish_line() { return 65536UL * length - 32768; }
uint32_t apparent_length() { return 65536UL * (length - 3); }
private:
int16_t length;
int16_t gravity;
int16_t o2_amount;
uint16_t fuel_amount;
uint32_t palette[72];
uint16_t tiles[32620];
};
struct rom_level
{
rom_level(const std::vector<char>& data, size_t offset, size_t length);
int16_t get_gravity() { return gravity; }
int16_t get_o2_amount() { return o2_amount; }
uint16_t get_fuel_amount() { return fuel_amount; }
uint16_t get_length() { return tiles.size() / 7; }
uint32_t get_palette_color(size_t idx) { return (idx < 72) ? palette[idx] : 0; }
tile get_tile(size_t idx) { return (idx < tiles.size()) ? tiles[idx] : tile(); }
void sha256_hash(uint8_t* buffer);
private:
int16_t gravity;
int16_t o2_amount;
uint16_t fuel_amount;
uint32_t palette[72];
std::vector<tile> tiles;
tile blank;
};
struct roads_lzs
{
roads_lzs();
~roads_lzs();
roads_lzs(const std::vector<char>& file);
roads_lzs(const roads_lzs& l);
roads_lzs& operator=(const roads_lzs& l);
bool present(size_t idx) { return (idx < 31 && l[idx] != NULL); }
rom_level& operator[](size_t idx) { return *l[idx]; }
private:
rom_level* l[31];
};
}
#endif

432
src/emulation/sky/logic.cpp Normal file
View file

@ -0,0 +1,432 @@
#include "logic.hpp"
#include "framebuffer.hpp"
#include "music.hpp"
#include "messages.hpp"
#include "romimage.hpp"
#include "draw.hpp"
#include "core/window.hpp"
#include "library/zip.hpp"
#define DEMO_WAIT 1080
namespace sky
{
const uint8_t hash1[32] = {
66,234,24,74,51,217,34,32,61,153,253,130,17,157,160,183,62,231,155,167,135,216,249,116,41,95,216,
97,168,163,129,23
};
const uint8_t hash2[32] = {
68,57,232,117,27,127,113,87,161,78,208,193,235,97,13,131,227,152,229,127,31,114,47,235,97,248,103,
11,159,217,129,136
};
uint32_t fadeffect_buffer[FB_WIDTH * FB_HEIGHT];
bool indirect_flag;
uint32_t framecnt_to_songno(uint64_t fc)
{
return fc;
}
void reload_song(bool menu, uint32_t num)
{
if(menu) {
mplayer.set_song(NULL);
if(bsong)
delete bsong;
bsong = NULL;
std::string filename = rom_filename + "/menu.opus";
std::istream* s = NULL;
try {
s = &open_file_relative(filename, "");
bsong = new background_song(*s);
mplayer.set_song(bsong);
delete s;
} catch(std::exception& e) {
if(s)
delete s;
}
} else {
mplayer.set_song(NULL);
if(bsong)
delete bsong;
bsong = NULL;
std::istream* s = NULL;
try {
zip_reader r(rom_filename);
std::string iname;
std::vector<std::string> inames;
for(auto i : r)
if(regex_match("music[0-9]+.opus", i))
inames.push_back(i);
if(inames.empty())
return;
iname = inames[num % inames.size()];
s = &r[iname];
bsong = new background_song(*s);
mplayer.set_song(bsong);
delete s;
} catch(std::exception& e) {
if(s)
delete s;
}
}
}
void faded_framebuffer(uint16_t alpha)
{
indirect_flag = true;
for(unsigned i = 0; i < sizeof(framebuffer) / sizeof(framebuffer[0]); i++) {
uint32_t L = (((framebuffer[i] & 0x00FF00FF) * alpha) >> 8) & 0x00FF00FF;
uint32_t H = (((framebuffer[i] & 0xFF00FF00U) >> 8) * alpha) & 0xFF00FF00U;
fadeffect_buffer[i] = L | H;
}
}
uint16_t stage_to_xpos(uint8_t stage)
{
return (stage > 15) ? 224 : 64;
}
uint16_t stage_to_ypos(uint8_t stage)
{
return 13 + (((stage - 1) % 15) / 3) * 39 + ((stage - 1) % 3) * 9;;
}
uint8_t do_menu_fadein(gstate& s, uint16_t b)
{
faded_framebuffer(8 * s.fadecount);
return (++s.fadecount == 32) ? state_menu : state_menu_fadein;
}
uint8_t do_menu_fadeout(gstate& s, uint16_t b)
{
faded_framebuffer(256 - 8 * s.fadecount);
return (++s.fadecount == 32) ? state_load_level : state_menu_fadeout;
}
uint8_t do_level_fadein(gstate& s, uint16_t b)
{
faded_framebuffer(8 * s.fadecount);
return (++s.fadecount == 32) ? state_level_play : state_level_fadein;
}
uint8_t do_level_fadeout(gstate& s, uint16_t b)
{
faded_framebuffer(256 - 8 * s.fadecount);
return (++s.fadecount == 32) ? state_load_menu : state_level_fadeout;
}
uint8_t do_level_fadeout_retry(gstate& s, uint16_t b)
{
faded_framebuffer(256 - 8 * s.fadecount);
return (++s.fadecount == 32) ? state_load_level_nomus : state_level_fadeout_retry;
}
uint8_t do_level_unavail(gstate& s, uint16_t b)
{
indirect_flag = false;
if(!s.fadecount) {
memset(framebuffer, 0, sizeof(framebuffer));
memset(origbuffer, 0, sizeof(origbuffer));
draw_message(_lvlunavail_g, 0xFFFFFF, 0);
}
s.fadecount = 1;
return ((b & ~s.lastkeys) & 0x30) ? state_load_menu : state_level_unavail;
}
uint8_t do_demo_unavail(gstate& s, uint16_t b)
{
indirect_flag = false;
if(!s.fadecount) {
memset(framebuffer, 0, sizeof(framebuffer));
memset(origbuffer, 0, sizeof(origbuffer));
draw_message(_demounavail_g, 0xFFFFFF, 0);
}
s.fadecount = 1;
return ((b & ~s.lastkeys) & 0x30) ? state_load_menu : state_demo_unavail;
}
uint8_t do_level_complete(gstate& s, uint16_t b)
{
indirect_flag = false;
if(!s.fadecount) {
if(s.secret == 0x81 && !s.demo_flag)
draw_message(_lvlcomplete2_g, 0xFFFFFF, 0);
else if(s.secret == 0x82 && !s.demo_flag)
draw_message(_lvlcomplete3_g, 0xFFFFFF, 0);
else
draw_message(_lvlcomplete_g, 0xFFFFFF, 0);
if(!s.demo_flag && s.stage > 0 && s.sram[s.stage] < 255)
s.sram[s.stage]++;
if(!s.demo_flag && s.stage > 0 && s.stage < 30)
s.stage++;
}
if(b & 112)
return state_level_fadeout;
return (++s.fadecount == 48) ? state_level_fadeout : state_level_complete;
}
uint8_t do_menu(gstate& s, uint16_t b)
{
indirect_flag = false;
if(s.savestage) {
s.stage = s.savestage;
s.savestage = 0;
}
if(s.stage == 0)
s.stage = 1;
if(s.oldstage != s.stage) {
if(s.oldstage) {
//Erase old mark.
uint16_t txpos = stage_to_xpos(s.oldstage);
uint16_t typos = stage_to_ypos(s.oldstage);
render_framebuffer_update(txpos, typos, 45, 7);
}
s.oldstage = s.stage;
uint16_t txpos = stage_to_xpos(s.stage);
uint16_t typos = stage_to_ypos(s.stage);
uint32_t color = origbuffer[320 * typos + txpos];
for(unsigned i = 0; i < FB_SCALE * 7; i++)
for(unsigned j = 0; j < FB_SCALE * 45; j++) {
size_t p = (i + FB_SCALE * typos) * FB_WIDTH + (j + FB_SCALE * txpos);
if(framebuffer[p] == color)
framebuffer[p] = 0xFFC080;
}
}
uint16_t rising = b & ~s.lastkeys;
if(rising & 1) //Left.
s.stage = (s.stage > 15) ? (s.stage - 15) : (s.stage + 15);
if(rising & 2) //Right.
s.stage = (s.stage > 15) ? (s.stage - 15) : (s.stage + 15);
if(rising & 4) //Up.
s.stage = ((s.stage % 15) == 1) ? (s.stage + 14) : (s.stage - 1);
if(rising & 8) //Down.
s.stage = ((s.stage % 15) == 0) ? (s.stage - 14) : (s.stage + 1);
if(rising & 48) { //A or Start.
if((b & 3) == 3) {
s.savestage = s.stage;
s.stage = 0;
}
s.demo_flag = ((b & 64) != 0);
return state_menu_fadeout;
}
if(b != s.lastkeys)
s.waited = 0;
else
if(++s.waited == DEMO_WAIT) {
s.savestage = s.stage;
s.stage = 0;
s.demo_flag = 2;
return state_menu_fadeout;
}
return state_menu;
}
uint8_t do_load_menu(gstate& s, uint16_t b)
{
s.cursong = framecnt_to_songno(s.frames_ran);
reload_song(true, s.cursong);
mplayer.rewind();
s.oldstage = 0;
for(unsigned i = 0; i < 64000; i++)
origbuffer[i] = levelselect[i];
render_backbuffer();
for(unsigned i = 1; i < 31; i++)
for(unsigned j = 0; j < s.sram[i] && j < 7; j++)
draw_bitmap(complete_mark, 3 * stage_to_xpos(i) + 21 * j + 141,
3 * stage_to_ypos(i) + 3);
indirect_flag = true;
memset(fadeffect_buffer, 0, sizeof(fadeffect_buffer));
s.waited = 0;
return state_menu_fadein;
}
uint8_t do_load_level(gstate& s, uint16_t b)
{
s.secret = 0;
if(s.state == state_load_level) {
s.cursong = framecnt_to_songno(s.frames_ran);
reload_song(false, s.cursong);
mplayer.rewind();
}
indirect_flag = true;
memset(fadeffect_buffer, 0, sizeof(fadeffect_buffer));
if(!levels.present(s.stage)) {
return state_level_unavail;
}
s.curlevel = level(levels[s.stage]);
if((s.curlevel.get_o2_amount() * 36) % 65536 == 0 || s.curlevel.get_fuel_amount() == 0)
return state_lockup;
s.level_init(s.stage);
combine_background(s.stage ? (s.stage - 1) / 3 : 0);
uint8_t hash[32];
levels[s.stage].sha256_hash(hash);
std::cerr << std::endl;
if(!memcmp(hash, hash1, 32)) s.secret = 1;
if(!memcmp(hash, hash2, 32)) s.secret = 2;
try {
if(s.demo_flag == 2) {
s.curdemo = builtin_demo;
} else if(s.demo_flag) {
s.curdemo = lookup_demo(hash);
} else {
s.curdemo = demo();
}
} catch(...) {
return state_demo_unavail;
}
rebuild_pipe_quad_caches(s.curlevel.get_palette_color(68), s.curlevel.get_palette_color(69),
s.curlevel.get_palette_color(70), s.curlevel.get_palette_color(71));
draw_grav_g_meter(s);
draw_level(s);
return state_level_fadein;
}
uint8_t do_level_play(gstate& s, uint16_t b)
{
indirect_flag = false;
//Handle demo.
b = s.curdemo.fetchkeys(b, s.p.lpos, s.p.framecounter);
if((b & 96) == 96) {
s.p.death = physics::death_escaped;
return state_level_fadeout;
}
if((b & ~s.lastkeys) & 32)
s.paused = s.paused ? 0 : 1;
if(s.paused)
return state_level_play;
int lr = 0, ad = 0;
bool jump = ((b & 16) != 0);
if((b & 1) != 0) lr--;
if((b & 2) != 0) lr++;
if((b & 4) != 0) ad++;
if((b & 8) != 0) ad--;
if((b & 256) != 0) lr = 2; //Cheat for demo.
if((b & 512) != 0) ad = 2; //Cheat for demo.
uint8_t death = s.simulate_frame(lr, ad, jump);
draw_level(s);
draw_gauges(s);
if(death == physics::death_finished)
return state_level_complete;
else if(death)
return state_level_fadeout_retry;
else
return state_level_play;
}
uint8_t do_lockup(gstate& s, uint16_t b)
{
mplayer.set_song(NULL);
return state_lockup;
}
void lstate_lockup(gstate& s)
{
mplayer.set_song(NULL);
memset(framebuffer, 0, sizeof(framebuffer));
memset(origbuffer, 0, sizeof(origbuffer));
}
void lstate_demo_unavail(gstate& s)
{
mplayer.set_song(NULL);
memset(framebuffer, 0, sizeof(framebuffer));
memset(origbuffer, 0, sizeof(origbuffer));
draw_message(_demounavail_g, 0xFFFFFF, 0);
}
void lstate_level_unavail(gstate& s)
{
mplayer.set_song(NULL);
memset(framebuffer, 0, sizeof(framebuffer));
memset(origbuffer, 0, sizeof(origbuffer));
draw_message(_lvlunavail_g, 0xFFFFFF, 0);
}
void lstate_level(gstate& s)
{
reload_song(false, s.cursong);
mplayer.do_preroll();
combine_background(s.stage ? (s.stage - 1) / 3 : 0);
s.speedind = 0;
s.fuelind = 0;
s.o2ind = 0;
s.distind = 0;
s.lockind = 0;
level& c = s.curlevel;
rebuild_pipe_quad_caches(c.get_palette_color(68), c.get_palette_color(69), c.get_palette_color(70),
c.get_palette_color(71));
draw_grav_g_meter(s);
draw_gauges(s);
draw_level(s);
}
void lstate_menu(gstate& s)
{
reload_song(true, s.cursong);
mplayer.do_preroll();
for(unsigned i = 0; i < 64000; i++)
origbuffer[i] = levelselect[i];
render_backbuffer();
for(unsigned i = 1; i < 31; i++)
for(unsigned j = 0; j < s.sram[i] && j < 7; j++)
draw_bitmap(complete_mark, FB_SCALE * stage_to_xpos(i) + 21 * j + FB_SCALE * 47,
FB_SCALE * stage_to_ypos(i) + FB_SCALE);
if(s.state == state_menu || s.state == state_menu_fadeout) {
uint16_t txpos = stage_to_xpos(s.stage);
uint16_t typos = stage_to_ypos(s.stage);
uint32_t color = origbuffer[320 * typos + txpos];
for(unsigned i = 0; i < FB_SCALE * 7; i++)
for(unsigned j = 0; j < FB_SCALE * 45; j++) {
size_t p = (i + FB_SCALE * typos) * FB_WIDTH + (j + FB_SCALE * txpos);
if(framebuffer[p] == color)
framebuffer[p] = 0xFFC080;
}
}
}
typedef uint8_t (*do_state_t)(gstate& s, uint16_t b);
do_state_t statefn[] = {
do_menu_fadein,do_menu,do_menu_fadeout,do_load_level,do_level_fadein,do_level_play,
do_level_complete, do_level_fadeout,do_load_menu,do_level_unavail,do_demo_unavail,
do_level_fadeout_retry, do_load_level, do_lockup
};
typedef void (*do_lstate_t)(gstate& s);
do_lstate_t lstatefn[] = {
lstate_menu,lstate_menu,lstate_menu,NULL,lstate_level,lstate_level,
lstate_level,lstate_level,NULL,lstate_level_unavail,lstate_demo_unavail,
lstate_level, lstate_level, lstate_lockup
};
void rom_boot_vector(gstate& s)
{
s.stage = s.savestage = s.oldstage = 0;
s.change_state(state_load_menu);
}
void simulate_frame(gstate& s, uint16_t b)
{
uint8_t retstate;
s.frames_ran++;
if(s.state > state_lockup) {
messages << "Invalid state in simulate: " << (int)s.state << std::endl;
s.change_state(state_load_menu); //Reboot.
}
retstate = statefn[s.state](s, b);
if(retstate != s.state)
s.change_state(retstate);
s.lastkeys = b;
}
void handle_loadstate(gstate& s)
{
messages << "Loadstate status: " << (int)s.state << std::endl;
if(s.state > state_lockup) {
messages << "Invalid state in loadstate: " << (int)s.state << std::endl;
s.change_state(state_load_menu); //Reboot.
}
if(lstatefn[s.state])
lstatefn[s.state](s);
}
}

View file

@ -0,0 +1,17 @@
#ifndef _skycore__logic__hpp__included__
#define _skycore__logic__hpp__included__
#include <cstdlib>
#include <cstdint>
#include "state.hpp"
#include "framebuffer.hpp"
namespace sky
{
void simulate_frame(gstate& s, uint16_t b);
void rom_boot_vector(gstate& s);
void handle_loadstate(gstate& s);
extern uint32_t fadeffect_buffer[FB_WIDTH * FB_HEIGHT];
extern bool indirect_flag;
}
#endif

137
src/emulation/sky/lzs.cpp Normal file
View file

@ -0,0 +1,137 @@
#include "lzs.hpp"
#include <cassert>
#include <climits>
#include <vector>
namespace sky
{
data_error::data_error(const char* errmsg) : std::runtime_error(errmsg) {}
input_stream::~input_stream()
{
}
inline void reload(input_stream& in, unsigned char& byte, unsigned char& bits)
{
assert(bits <= 8);
if(bits == 0) {
int r = in.read("Unexpected end of compressed data");
byte = r;
bits = 8;
}
assert(bits <= 8);
}
inline bool read1(input_stream& in, unsigned char& byte, unsigned char& bits)
{
assert(bits <= 8);
reload(in, byte, bits);
bool ret = ((byte & 0x80) != 0);
byte <<= 1;
bits--;
assert(bits <= 8);
return ret;
}
inline size_t read_n(input_stream& in, unsigned char& byte, unsigned char& bits, unsigned count)
{
assert(bits <= 8);
if(count == 0)
return 0;
if(count <= bits) {
//Satisfiable immediately.
size_t ret = byte >> (8 - count);
bits -= count;
byte <<= count;
return ret;
}
size_t readc = 0;
size_t ret = 0;
//Read the first incomplete byte if any.
if(bits > 0) {
ret = byte >> (8 - bits);
readc = bits;
bits = 0;
}
//Read complete bytes.
while(readc + 8 <= count) {
int r = in.read("Unexpected end of compressed data");
ret = (ret << 8) | (r & 0xFF);
readc += 8;
}
size_t tailbits = count - readc;
if(tailbits > 0) {
//Read the tail bits.
reload(in, byte, bits);
ret = (ret << tailbits) | (byte >> (8 - tailbits));
byte <<= tailbits;
bits -= tailbits;
}
return ret;
}
void lzs_decompress(input_stream& in, output_stream& out, size_t size)
{
unsigned char cbits = in.read("Incomplete compression header");
unsigned char sbits = in.read("Incomplete compression header");
unsigned char lbits = in.read("Incomplete compression header");
size_t maxbits = sizeof(size_t) * CHAR_BIT;
if((sbits >= maxbits || lbits >= maxbits) || (sbits == maxbits - 1 && lbits == maxbits - 1))
throw data_error("Can't handle the backward offset space");
if(cbits >= maxbits)
throw data_error("Can't handle the copy length space");
size_t maxoffset = 2 + (size_t(1) << sbits) + (size_t(1) << lbits);
size_t shortbias = 2;
size_t longbias = 2 + (size_t(1) << sbits);
maxoffset = (size < maxoffset) ? size : maxoffset;
std::vector<unsigned char> tmp;
tmp.resize(maxoffset);
uint8_t pending_byte = 0;
uint8_t pending_bits = 0;
int pending_mode = 0;
size_t output = 0;
size_t copy_remaining = 0;
size_t copy_backward = 0;
size_t cyclic_pointer = 0;
while(output < size) {
assert(pending_bits <= 8);
if(copy_remaining) {
size_t cindex;
if(copy_backward <= cyclic_pointer)
cindex = cyclic_pointer - copy_backward;
else
cindex = maxoffset + cyclic_pointer - copy_backward;
copy_remaining--;
out.put(tmp[cyclic_pointer++] = tmp[cindex]);
if(cyclic_pointer == maxoffset)
cyclic_pointer = 0;
output++;
continue;
}
if(read1(in, pending_byte, pending_bits)) {
if(read1(in, pending_byte, pending_bits)) {
//Literial insert.
out.put(tmp[cyclic_pointer++] = static_cast<unsigned char>(read_n(in,
pending_byte, pending_bits, 8)));
if(cyclic_pointer == maxoffset)
cyclic_pointer = 0;
output++;
} else {
//Long copy.
copy_backward = longbias + read_n(in, pending_byte, pending_bits, lbits);
copy_remaining = 2 + read_n(in, pending_byte, pending_bits, cbits);
if(copy_backward > output)
throw data_error("Backward copy offset too large");
}
} else {
//Short copy.
copy_backward = shortbias + read_n(in, pending_byte, pending_bits, sbits);
copy_remaining = 2 + read_n(in, pending_byte, pending_bits, cbits);
if(copy_backward > output)
throw data_error("Backward copy offset too large");
}
}
}
}

35
src/emulation/sky/lzs.hpp Normal file
View file

@ -0,0 +1,35 @@
#ifndef _skycore__lzs__hpp__included__
#define _skycore__lzs__hpp__included__
#include <cstdint>
#include <iostream>
#include <cstring>
#include <stdexcept>
namespace sky
{
struct data_error : public std::runtime_error
{
data_error(const char* errmsg);
};
struct input_stream
{
virtual ~input_stream();
virtual int get() = 0;
unsigned char read(const char* msg)
{
int r = get();
if(r < 0) throw data_error(msg);
return (r & 0xFF);
}
};
struct output_stream
{
virtual void put(unsigned char byte) = 0;
};
void lzs_decompress(input_stream& in, output_stream& out, size_t size);
}
#endif

View file

@ -0,0 +1,51 @@
#include "messages.hpp"
namespace sky
{
const char* _lockind_g = "%=(bFA*cN:Rc^X[cbPAccKSD`%=(4c='(RYR@>aK49c>XXRSD#";
const char* _numbers_g = "#'(#;/;##'(JVJVJ#'(#V#<##'(#V#V##'(/;#VJ#'(#<#V##'(#<#;##'(#VJVJ#'(#;#;##'(#;#V#";
const char* _lvlcomplete_g = "$e5-###$#####%###.S##'&C###+%##KC##34####C+#%E112%#?aVU@]T_T'('+%-+KM-+KL'6W"
"3C+KEERKbEE444$'EE--CE%-']``&DU+R,_JJC########C############%#####";
const char* _lvlunavail_g = "$e#+#####'####-'####3####O3###$####%L*$^,ZC]TT^GE'+K4%+CKL5+1ED'+EAEF+C--'3E-"
"--'?*JJ4%*2JC";
const char* _demounavail_g = "$e)+#####'#%###%'####3#+###/3###$##C###L*$^,ZCA?aTUGE'+K4%--+KL5+1ED'+RKEEF+"
"C--'3ECE--'?*JJ4$]^+J#";
const char* _lvlcomplete2_g = "$eE-############$###+3####C######3##&'####+######'###L*#[]cJ1&FVZD[]VM5+4$3"
"E-$(((#444)7E*VVKbCB441':WBF+D$'-++''73D($'+4S2BA]^#``a[423`#########$$##############&FC#####";
const char* _lvlcomplete3_g = "$eW-#####%#######K+$###+#####C#C####+%'3##%#####+#+####%#D'###C`[[,.TZD']'*"
"L[a[]VK+44%ME%C444%-E'444)$ZZSMKJK%G73EKD':WBC($#'5--#L('+M+4($'+222$'Eb['12$^A*423`########$####"
"##############$C##########";
const uint32_t complete_mark[] = {
18, 15,
0x000000, 0x000000, 0x000000, 0x000000, 0x613e1a, 0xb67a3d, 0xeaa45f, 0xfcb977, 0xeab47f,
0xb6926e, 0x615141, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0x2e1903, 0xc7711b, 0xff9934, 0xffa245, 0xffab56, 0xffb367, 0xffbc78,
0xffc489, 0xffcd9a, 0xc7a686, 0x2e2822, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
0x000000, 0x2d1600, 0xe97401, 0xff8912, 0xff9123, 0xff9a34, 0xffa145, 0xffab56, 0xffb367,
0xffbc79, 0xffc489, 0xffcd9b, 0xe9c39c, 0x2e2822, 0x000000, 0x000000, 0x000000, 0x000000,
0x000000, 0xba5d00, 0xf77c00, 0xff8000, 0xff8812, 0xff9123, 0xff9934, 0xffa245, 0xffab56,
0xffb367, 0xffbb77, 0xffc489, 0xffcd9a, 0xc7a685, 0x000000, 0x000000, 0x000000, 0x000000,
0x542a00, 0xe67300, 0xee7700, 0xf77c00, 0xff7f01, 0xff8812, 0xff9023, 0xff9933, 0xffa245,
0xffaa56, 0xffb367, 0xffbc78, 0xffc489, 0xffcd9a, 0x615141, 0x000000, 0x000000, 0x000000,
0x974c00, 0xdd6e00, 0xe67300, 0xee7700, 0xf77b00, 0xff7f01, 0xff8811, 0xff9123, 0xff9934,
0xffa244, 0xffab56, 0xffb267, 0xffbb78, 0xffc489, 0xb6926e, 0x000000, 0x000000, 0x000000,
0xbb5e00, 0xd56a00, 0xdd6e00, 0xe67200, 0xee7700, 0xf77b00, 0xff8000, 0xff8812, 0xff9123,
0xff9933, 0xffa245, 0xffaa56, 0xffb367, 0xffbb78, 0xeab47e, 0x000000, 0x000000, 0x000000,
0xc26100, 0xcc6600, 0xd46b00, 0xdd6e00, 0xe67200, 0xee7700, 0xf67b00, 0xff8001, 0xff8811,
0xff9023, 0xff9933, 0xffa244, 0xffab55, 0xffb367, 0xfcba76, 0x000000, 0x000000, 0x000000,
0xac5600, 0xc46100, 0xcc6600, 0xd56a00, 0xde6f00, 0xe67200, 0xee7700, 0xf67c00, 0xff8001,
0xff8811, 0xff9023, 0xff9934, 0xffa244, 0xffaa56, 0xeaa45f, 0x000000, 0x000000, 0x000000,
0x804000, 0xbb5d00, 0xc36200, 0xcc6600, 0xd46a00, 0xdd6f00, 0xe57300, 0xef7700, 0xf77b00,
0xff8000, 0xff8812, 0xff9122, 0xff9934, 0xffa244, 0xb6793d, 0x000000, 0x000000, 0x000000,
0x412000, 0xb25900, 0xbb5e00, 0xc46100, 0xcc6600, 0xd56a00, 0xdd6e00, 0xe57300, 0xee7700,
0xf67c00, 0xff7f01, 0xff8912, 0xff9123, 0xff9933, 0x613e1a, 0x000000, 0x000000, 0x000000,
0x000000, 0x854200, 0xb25900, 0xba5e00, 0xc36200, 0xcc6600, 0xd56b00, 0xdd6e00, 0xe67200,
0xee7700, 0xf67b00, 0xff8000, 0xff8812, 0xc7701b, 0x000000, 0x000000, 0x000000, 0x000000,
0x000000, 0x1d0e00, 0x9b4e00, 0xb35900, 0xbb5d00, 0xc46200, 0xcc6600, 0xd46b00, 0xdd6e00,
0xe57300, 0xee7700, 0xf67b00, 0xe97401, 0x2e1903, 0x000000, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0x1d0e00, 0x854200, 0xb25a00, 0xbb5d00, 0xc46200, 0xcc6600, 0xd46a00,
0xdc6f00, 0xe67300, 0xba5d00, 0x2d1600, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0x000000, 0x000000, 0x412000, 0x804000, 0xac5600, 0xc16100, 0xbb5d00,
0x984c00, 0x542a00, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
};
}

View file

@ -0,0 +1,19 @@
#ifndef _skycore__messages__hpp__included__
#define _skycore__messages__hpp__included__
#include <cstdlib>
#include <cstdint>
namespace sky
{
extern const char* _lockind_g;
extern const char* _numbers_g;
extern const char* _lvlcomplete_g;
extern const char* _lvlcomplete2_g;
extern const char* _lvlcomplete3_g;
extern const char* _lvlunavail_g;
extern const char* _demounavail_g;
extern const uint32_t complete_mark[];
}
#endif

342
src/emulation/sky/music.cpp Normal file
View file

@ -0,0 +1,342 @@
#include "music.hpp"
#include <cstring>
#include <cmath>
#include "library/minmax.hpp"
#include "library/ogg.hpp"
#include "library/string.hpp"
#include "core/window.hpp"
namespace sky
{
const uint64_t past_end = 0xFFFFFFFFFFFFFFFFULL;
background_song* bsong = NULL;
uint8_t ticks_per_frame[32] = {
4, 8, 16, 24,
4, 8, 16, 24,
4, 8, 16, 24,
4, 8, 4, 8,
1, 2, 4, 8,
1, 2, 4, 8,
1, 2, 4, 8,
1, 2, 4, 8
};
uint8_t opus_packet_tick_count(const uint8_t* packet, size_t packetsize)
{
if(packetsize < 1)
return 0;
uint8_t x = ticks_per_frame[packet[0] >> 3];
uint8_t y = (packetsize < 2) ? 255 : (packet[1] & 0x3F);
uint16_t z = (uint16_t)x * y;
switch(packet[0] & 3) {
case 0: return x;
case 1: return x << 1;
case 2: return x << 1;
case 3: return (z <= 48) ? z : 0;
};
}
uint64_t background_song::find_timecode_down(uint64_t pts)
{
if(pts == past_end)
return past_end;
auto i = packets.upper_bound(pts);
if(i == packets.end())
return packets.rbegin()->first;
else {
i--;
return i->first;
}
}
uint64_t background_song::find_timecode_up(uint64_t pts)
{
if(pts == past_end)
return past_end;
auto i = packets.lower_bound(pts);
if(i == packets.end())
return past_end;
else
return i->first;
}
uint64_t background_song::pcm_to_pts1(uint64_t pcm)
{
// | |
//1: PPIIIIIILLLLLLC LLLLLLC LLLLLLC LLLLLLC L...
//2: LLLLLLC LLLLLLC LLLLLLC LLLLLLC...
uint64_t C = total - crossfade_start;
uint64_t L = total - loop_start;
uint64_t LminusC = L - C;
uint64_t Lminus2C = L - 2 * C;
if(pcm == past_end)
return past_end;
pcm += pregap;
if(pcm < total)
return pcm;
pcm -= total;
pcm %= 2 * LminusC;
//There is first a gap.
if(pcm < Lminus2C)
return past_end;
pcm -= Lminus2C;
//Then there's a new looping section.
return pcm + loop_start;
}
uint64_t background_song::pcm_to_pts2(uint64_t pcm)
{
// | |
//1: PPIIIIIILLLLLLC LLLLLLC LLLLLLC LLLLLLC L...
//2: LLLLLLC LLLLLLC LLLLLLC LLLLLLC...
uint64_t C = total - crossfade_start;
uint64_t L = total - loop_start;
uint64_t LminusC = L - C;
uint64_t Lminus2C = L - 2 * C;
if(pcm == past_end)
return past_end;
pcm += pregap;
//Before crossfade_start, there's nothing.
if(pcm < crossfade_start)
return past_end;
pcm -= crossfade_start;
pcm %= 2 * LminusC;
//First there is loop region.
if(pcm < L)
return pcm + loop_start;
//Then there's a gap.
return past_end;
}
int32_t background_song::gain_factor()
{
if(!gain)
return 256;
double g = pow(10, gain / 20.0);
if(g >= 256)
return 65536;
return 256 * g;
}
background_song::background_song(std::istream& stream)
{
ogg_stream_reader_iostreams r(stream);
r.set_errors_to(messages);
ogg_page p;
uint64_t pnum = 0;
bool loop_spec = false, cf_spec = false;
loop_start = 0;
crossfade_start = 0;
std::vector<uint8_t> pending_data;
uint64_t last_gpos = ogg_page::granulepos_none;
uint64_t rpos = 0;
uint64_t old_rpos = 0;
while(r.get_page(p)) {
if(pnum == 0) {
//Header page.
struct oggopus_header h = parse_oggopus_header(p);
if(h.map_family != 0)
(stringfmt() << "Unsupported mapping family " << h.map_family).throwex();
pregap = h.preskip;
gain = h.gain;
} else if(pnum == 1) {
//Tags page.
struct oggopus_tags t = parse_oggopus_tags(p);
for(auto i : t.comments) {
regex_results r = regex("([^=]+)=(.*)", i);
if(!r)
continue;
if(r[1] == "LSNES_LOOP_START") {
loop_start = parse_value<uint64_t>(r[2]) + pregap;
loop_spec = true;
} else if(r[1] == "LSNES_XFADE_START") {
crossfade_start = parse_value<uint64_t>(r[2]) + pregap;
cf_spec = true;
}
}
} else {
//Data page.
uint64_t gpos = p.get_granulepos();
uint8_t pkts = p.get_packet_count();
bool e = p.get_eos();
bool c = p.get_continue();
bool i = p.get_last_packet_incomplete();
for(unsigned j = 0; j < pkts; j++) {
if(i > 0 || !c)
pending_data.clear();
size_t b = pending_data.size();
auto pkt = p.get_packet(j);
pending_data.resize(b + pkt.second);
memcpy(&pending_data[b], pkt.first, pkt.second);
if(i && j == pkts - 1)
break; //Next page.
//Pending_data is now opus packet.
uint8_t tcnt = opus_packet_tick_count(&pending_data[0], pending_data.size());
if(tcnt > 0)
packets[rpos] = pending_data;
rpos += 120 * tcnt;
total = rpos;
}
if(e) {
uint64_t pscnt = gpos - ((last_gpos == ogg_page::granulepos_none) ? 0 :
last_gpos);
total = old_rpos + pscnt;
if(total > rpos)
total = rpos;
break;
}
if(gpos != ogg_page::granulepos_none) {
last_gpos = gpos;
old_rpos = rpos;
}
}
pnum++;
}
if(!cf_spec)
crossfade_start = total;
if(!loop_spec)
loop_start = pregap;
if(loop_start >= total) {
messages << "Bad loop point, assuming start of song" << std::endl;
loop_start = pregap;
}
if(crossfade_start - loop_start < total - crossfade_start) {
messages << "Bad XFADE point, assuming end of song." << std::endl;
crossfade_start = total;
}
}
music_player_int::music_player_int()
#ifdef WITH_OPUS_CODEC
: d(opus::samplerate::r48k, true)
#endif
{
}
void music_player_int::decode_packet(const std::vector<uint8_t>& data)
{
pcmlen = 120 * opus_packet_tick_count(&data[0], data.size());
pcmpos = 0;
memset(&pcmbuf[0], 0, 11520 * sizeof(int16_t));
try {
d.decode(&data[0], data.size(), &pcmbuf[0], 5760);
} catch(std::exception& e) {
messages << "Music: Failed to decode opus packet: " << e.what() << std::endl;
}
}
void music_player::seek_channel(music_player_int& i, uint64_t& spts, uint64_t pts)
{
if(pts == past_end) {
i.pcmpos = i.pcmlen = 0;
spts = past_end;
return;
}
#ifdef WITH_OPUS_CODEC
i.d.ctl(opus::reset);
#endif
uint64_t ptsr = song->find_timecode_down((pts >= 3840) ? (pts - 3840) : 0);
while(ptsr < pts) {
ptsr = song->find_timecode_up(ptsr);
if(ptsr == past_end)
break;
i.decode_packet(song->packets[ptsr]);
if(ptsr + i.pcmlen > pts) {
i.pcmpos = pts - ptsr;
ptsr = pts;
} else
ptsr += i.pcmlen;
}
spts = pts;
}
void music_player::do_preroll()
{
if(!song || song->packets.empty())
return;
uint64_t pts1 = song->pcm_to_pts1(pcmpos);
uint64_t pts2 = song->pcm_to_pts2(pcmpos);
uint64_t pts1p;
uint64_t pts2p;
seek_channel(i1, pts1p, pts1);
seek_channel(i2, pts2p, pts2);
}
void music_player::decode(std::pair<int16_t, int16_t>* output, size_t samples)
{
if(!song) {
memset(output, 0, samples * sizeof(std::pair<int16_t, int16_t>));
pcmpos += samples;
return;
}
int32_t gfactor = builtin_gain ? 256 : song->gain_factor();
uint64_t pts1 = song->pcm_to_pts1(pcmpos);
uint64_t pts2 = song->pcm_to_pts2(pcmpos);
uint64_t cfstart = song->crossfade_start;
uint64_t cflen = song->total - song->crossfade_start;
for(; samples > 0; output++, samples--, pcmpos++) {
if(song->crossfade_start == pts1)
seek_channel(i2, pts2, song->loop_start);
if(song->crossfade_start == pts2)
seek_channel(i1, pts1, song->loop_start);
if(song->total == pts1)
seek_channel(i1, pts1, past_end);
if(song->total == pts2)
seek_channel(i2, pts2, past_end);
if(i1.pcmpos == i1.pcmlen && pts1 != past_end) {
uint64_t pts = song->find_timecode_up(pts1);
if(pts != past_end)
i1.decode_packet(song->packets[pts]);
}
if(i2.pcmpos == i2.pcmlen && pts2 != past_end) {
uint64_t pts = song->find_timecode_up(pts2);
if(pts != past_end)
i2.decode_packet(song->packets[pts]);
}
uint32_t cf = 0, icf = 0;
if(i1.pcmpos < i1.pcmlen)
cf = (pts1 > cfstart) ? (256 - 256 * (pts1 - cfstart) / cflen) : 256;
if(i2.pcmpos < i2.pcmlen)
icf = (pts2 > cfstart) ? (256 - 256 * (pts2 - cfstart) / cflen) : 256;
int32_t l = (cf * i1.pcmbuf[2 * i1.pcmpos + 0] + icf * i2.pcmbuf[2 * i2.pcmpos + 0]) >> 8;
int32_t r = (cf * i1.pcmbuf[2 * i1.pcmpos + 1] + icf * i2.pcmbuf[2 * i2.pcmpos + 1]) >> 8;
output->first = max(min((gfactor * l) >> 8, 32767), -32768);
output->second = max(min((gfactor * r) >> 8, 32767), -32768);
if(i1.pcmpos < i1.pcmlen)
i1.pcmpos++;
if(i2.pcmpos < i2.pcmlen)
i2.pcmpos++;
if(pts1 != past_end)
pts1++;
if(pts2 != past_end)
pts2++;
}
}
void music_player::set_gain()
{
if(!song)
return;
try {
#ifdef WITH_OPUS_CODEC
i1.d.ctl(opus::gain(song->gain));
i2.d.ctl(opus::gain(song->gain));
#endif
builtin_gain = true;
} catch(...) {
builtin_gain = false;
}
}
music_player::music_player(uint64_t& pcm)
: pcmpos(pcm)
{
song = NULL;
}
}

View file

@ -0,0 +1,99 @@
#ifndef _skycore__music__hpp__included__
#define _skycore__music__hpp__included__
#include <stdexcept>
#include <cstdint>
#include <vector>
#include <map>
#ifdef WITH_OPUS_CODEC
#include "library/opus.hpp"
#endif
//
//
//
//
//
//
namespace sky
{
struct song_buffer;
extern const uint64_t past_end; //Position past the end of song.
struct background_song
{
//Load song from stream.
background_song(std::istream& stream);
//The song data.
std::map<uint64_t, std::vector<uint8_t>> packets;
//Pregap length.
uint64_t pregap;
//Song length, including pregap.
uint64_t total;
//Loop start start pts.
uint64_t loop_start;
//Start of crossfade pts.
uint64_t crossfade_start;
//Gain.
uint16_t gain;
//Find valid timecode, rounding down.
uint64_t find_timecode_down(uint64_t pts);
//Find valid timecode, rounding up. Returns past_end if called with too great pts.
uint64_t find_timecode_up(uint64_t pts);
//Translate pcm position to pts for track 1.
uint64_t pcm_to_pts1(uint64_t pcm);
//Translate pcm position to pts for track 2.
uint64_t pcm_to_pts2(uint64_t pcm);
//Translate gain into gain factor.
int32_t gain_factor();
};
extern background_song* bsong;
uint8_t opus_packet_tick_count(const uint8_t* packet, size_t packetsize);
#ifdef WITH_OPUS_CODEC
typedef opus::decoder opus_decoder;
#else
struct opus_decoder
{
size_t decode(const uint8_t* a, size_t b, int16_t* c, size_t d)
{
uint8_t t = opus_packet_tick_count(a, b);
if(!t)
throw std::runtime_error("Bad packet");
return 120 * t;
}
};
#endif
struct music_player_int
{
music_player_int();
void decode_packet(const std::vector<uint8_t>& data);
uint16_t pcmpos;
uint16_t pcmlen;
int16_t pcmbuf[11522];
opus_decoder d;
};
struct music_player
{
music_player(uint64_t& _pcmpos);
void set_song(background_song* _song) { song = _song; set_gain(); }
void rewind() { pcmpos = 0; do_preroll(); }
void do_preroll();
void decode(std::pair<int16_t, int16_t>* output, size_t samples);
private:
void set_gain();
void seek_channel(music_player_int& i, uint64_t& spts, uint64_t pts);
bool builtin_gain;
uint64_t& pcmpos;
music_player_int i1;
music_player_int i2;
background_song* song;
};
}
#endif

View file

@ -0,0 +1,441 @@
#include "physics.hpp"
#include "sound.hpp"
#include "util.hpp"
namespace sky
{
noise_maker::~noise_maker()
{
}
void physics::force_flag(uint8_t b, bool s)
{
if(s)
flags |= b;
else
flags &= ~b;
}
void physics::set_flag(uint8_t b)
{
flags |= b;
}
void physics::clear_flag(uint8_t b)
{
flags &= ~b;
}
bool physics::is_masked(uint8_t b, uint8_t c)
{
return ((flags & b) == c);
}
bool physics::is_set(uint8_t b)
{
return ((flags & b) == b);
}
bool physics::is_any_of(uint8_t b)
{
return ((flags & b) != 0);
}
bool physics::is_clear(uint8_t b)
{
return ((flags & b) == 0);
}
void physics::adjust_speed(level& stage, int adjust) throw()
{
lspeed += adjust;
if(lspeed < 0)
lspeed = 0;
if(lspeed > 10922)
lspeed = 10922;
}
void physics::die(level& stage, uint8_t cause)
{
if(death)
return;
death = cause;
deathframe = framecounter;
}
void physics::explode(level& stage, noise_maker& noise)
{
if(expframe)
return;
expframe = 1;
noise(sound_explode);
}
void physics::apply_floor_effects(level& stage, noise_maker& noise, unsigned floor)
{
if(is_clear(flag_landed)) {
clear_flag(flag_sticky);
return;
}
switch(floor) {
case tile::sticky:
if(!expframe)
adjust_speed(stage, -303);
break;
case tile::suppiles:
if(death)
break;
if(o2_left < 27000 || fuel_left < 27000)
noise(sound_suppiles);
o2_left = 30000;
fuel_left = 30000;
break;
case tile::boost:
if(!expframe)
adjust_speed(stage, 303);
break;
case tile::burning:
die(stage, death_burning);
explode(stage, noise);
break;
}
force_flag(flag_slippery, floor == tile::slippery);
force_flag(flag_sticky, floor == tile::sticky);
}
void physics::check_exit(level& stage) throw()
{
if(lpos < stage.finish_line())
return;
if(!stage.in_pipe(lpos, hpos, vpos))
return;
//Die preserves death reason if one exists.
die(stage, death_finished);
}
void physics::use_suppiles(level& stage) throw()
{
if(death)
return;
o2_left -= o2_factor;
if(o2_left > 30000)
o2_left = 0;
fuel_left -= (fuel_factor * lspeed) / 65536;
if(fuel_left > 30000)
fuel_left = 0;
}
void physics::check_death(level& stage) throw()
{
if(!death) {
if(vpos < 10240)
die(stage, death_drifting);
else if(fuel_left == 0)
die(stage, death_fuel);
else if(o2_left == 0)
die(stage, death_o2);
} else
postdeath++;
if(expframe)
expframe++;
}
void physics::apply_steering(level& stage, int lr, int ad, bool jump) throw()
{
if(death)
return;
adjust_speed(stage, 75 * ad);
if(is_clear(flag_slippery)) {
if(is_any_of(flag_blank | flag_jumping)) {
if(hspeed == 0 && vspeed > 0 && vpos - jump_ground < 3840)
hspeed = 29 * lr;
} else
hspeed = 29 * lr;
}
if(jump && is_clear(flag_blank | flag_jumping | flag_no_jump)) {
vspeed = 1152;
set_flag(flag_jumping);
jump_ground = vpos;
}
}
void physics::apply_gravity(level& stage) throw()
{
if(!expframe) {
if(vpos >= 10240)
vspeed = vspeed + gravity_accel;
else if(vspeed > -106)
vspeed = -106;
} else {
if(vspeed < 0)
vspeed = 0;
else if(vspeed < 71)
vspeed += 39;
else
vspeed = 71;
}
}
void physics::project_position(level& stage) throw()
{
lprojected = lpos + lspeed;
hprojected = hpos + (hspeed * (lspeed + (is_set(flag_sticky) ? 0 : 1560))) / 512 + hdrift;
vprojected = vpos + vspeed;
//Don't wrap around horizontally.
if((hpos < 12160 && hprojected >= 53376) && (hpos >= 53376 && hprojected < 12160))
hprojected = hpos;
}
uint8_t physics::get_death(level& stage) throw()
{
if(!death)
return 0; //Still alive.
else if(death == death_finished) {
framecounter++;
lpos += lspeed;
return (++postdeath > 72) ? death : 0;
} else if(death == death_collided || death == death_burning)
return (expframe > 42) ? death : 0;
else if(death == death_drifting)
return (expframe > 42 || postdeath > 108) ? death : 0;
else
return (postdeath > 108) ? death : 0;
}
void physics::check_scratching(level& stage) throw()
{
if(hprojected == hpos)
return;
hspeed = 0;
if(hdrift < 0 && hpos > hprojected)
hdrift = 0;
if(hdrift > 0 && hpos < hprojected)
hdrift = 0;
adjust_speed(stage, -151);
}
void physics::check_collisions(level& stage, noise_maker& noise) throw()
{
if(lprojected == lpos)
return;
if(lspeed >= 3640) {
die(stage, death_collided);
explode(stage, noise);
} else {
if(lpos > lprojected - lspeed)
noise(sound_blow);
}
lspeed = 0;
}
void physics::try_locking(level& stage, int lr, int ad, bool jump) throw()
{
if(vpos < 14080 || !is_masked(flag_tried_lock | flag_jumping, flag_jumping))
return;
set_flag(flag_tried_lock);
if(!dangerous_jump(stage, ad, lpos, hpos, vpos, lspeed, hspeed, vspeed, hdrift))
return;
for(int32_t a = 1; a < 7; a++) {
if(!dangerous_jump(stage, ad, lpos, hpos, vpos, lspeed, hspeed + (a * hspeed) / 10, vspeed,
hdrift)) {
hspeed = hspeed + (a * hspeed) / 10;
set_flag(flag_locked);
return;
}
if(!dangerous_jump(stage, ad, lpos, hpos, vpos, lspeed, hspeed - (a * hspeed) / 10, vspeed,
hdrift)) {
hspeed = hspeed - (a * hspeed) / 10;
set_flag(flag_locked);
return;
}
int32_t tmp = lspeed + (a * lspeed) / 10;
if(tmp < 10922)
if(!dangerous_jump(stage, ad, lpos, hpos, vpos, tmp, hspeed, vspeed, hdrift)) {
speedbias = tmp - lspeed;
lspeed = tmp;
set_flag(flag_locked);
return;
}
if(!dangerous_jump(stage, ad, lpos, hpos, vpos, lspeed - (a * lspeed) / 10, hspeed, vspeed,
hdrift)) {
speedbias = - (a * lspeed) / 10;
lspeed = lspeed - (a * lspeed) / 10;
set_flag(flag_locked);
return;
}
}
}
void physics::check_horizontal_eject(level& stage, noise_maker& noise) throw()
{
if(lprojected == lpos || hpos != hprojected)
return;
if(!stage.collides(lprojected, hpos, vpos))
return;
if(!stage.collides(lprojected, hpos - 928, vpos)) {
hpos -= 928;
lprojected = lpos;
noise(sound_blow);
} else if(!stage.collides(lprojected, hpos + 928, vpos)) {
hpos += 928;
lprojected = lpos;
noise(sound_blow);
}
}
void physics::apply_bounce(level& stage, noise_maker& noise) throw()
{
if(vprojected == vpos)
return;
if(hdrift != 0 && nosupport < 2) {
vspeed = 0;
return;
}
if(expframe || std::abs(vspeed) < bounce_limit) {
vspeed = 0;
return;
}
if(!death && vspeed < 0)
noise(sound_bounce);
vspeed = -((5 * static_cast<int16_t>(vspeed)) / 10);
}
void physics::check_landing(level& stage) throw()
{
if(vprojected == vpos || vspeed >= 0)
return;
clear_flag(flag_locked | flag_tried_lock | flag_jumping);
set_flag(flag_landed);
adjust_speed(stage, -speedbias);
speedbias = 0;
nosupport_d = 0;
for(int i = 1; i <= 14; i++)
if(!stage.collides(lpos, hpos + 128 * i, vpos - 1)) {
nosupport = i;
nosupport_d++;
break;
}
for(int i = 1; i <= 14; i++)
if(!stage.collides(lpos, hpos - 128 * i, vpos - 1)) {
nosupport = i;
nosupport_d--;
break;
}
if(nosupport_d)
hdrift += 17 * nosupport_d;
else
hdrift = 0;
}
void physics::move_ship(level& stage) throw()
{
if(lprojected == lpos && hprojected == hpos && vprojected == vpos)
return;
uint32_t tmp_l;
uint16_t tmp_h;
int32_t tmp_v;
int32_t delta_l = lprojected - lpos;
int16_t delta_h = hprojected - hpos;
int16_t delta_v = vprojected - vpos;
int i;
for(i = 1; i <= 5; i++) {
tmp_l = lpos + i * delta_l / 5;
tmp_h = hpos + i * delta_h / 5;
tmp_v = vpos + i * delta_v / 5;
if(stage.collides(tmp_l, tmp_h, tmp_v)) {
break;
}
}
lpos = lpos + (i - 1) * delta_l / 5;
hpos = hpos + (i - 1) * delta_h / 5;
vpos = vpos + (i - 1) * delta_v / 5;
for(i = 16384; i > 0; i /= 2)
if(i <= abs(lprojected - lpos))
if(!stage.collides(lpos + sgn(delta_l) * i, hpos, vpos))
lpos = lpos + sgn(delta_l) * i;
for(i = 16384; i > 0; i /= 2)
if(i <= abs(hprojected - hpos))
if(!stage.collides(lpos, hpos + sgn(delta_h) * i, vpos))
hpos = hpos + sgn(delta_h) * i;
for(i = 16384; i > 0; i /= 2)
if(i <= abs(vprojected - vpos))
if(!stage.collides(lpos, hpos, vpos + sgn(delta_v) * i))
vpos = vpos + sgn(delta_v) * i;
}
bool physics::dangerous_jump(level& stage, int ad, uint32_t lp, uint16_t hp, int16_t vp, int32_t lv,
int16_t hv, int16_t vv, int16_t hd)
{
uint16_t old_hp;
uint32_t old_lp;
do {
old_hp = hp;
old_lp = lp;
vv = vv + gravity_accel;
lp = lp + lv;
hp = hp + hv * (lv + 1560) / 512 + hd;
if(hp < 12160 || hp > 53376)
return true;
vp = vp + vv;
lv = lv + 75 * ad;
if(lv < 0)
lv = 0;
if(lv > 10922)
lv = 10922;
} while(vp > 10240);
tile A = stage.at(old_lp, old_hp);
tile B = stage.at(lp, hp);
return A.is_dangerous() || B.is_dangerous();
}
uint8_t physics::simulate_frame(level& stage, noise_maker& noise, int lr, int ad, bool jump)
{
uint8_t cod = get_death(stage);
if(cod)
return cod;
if(death == death_finished)
return 0; //The animation to scroll.
tile t = stage.at(lpos, hpos);
force_flag(flag_blank, t.is_blank());
apply_floor_effects(stage, noise, t.surface_type(vpos));
check_exit(stage);
if(death == death_finished)
return 0; //If check_exit changed things.
apply_bounce(stage, noise);
apply_steering(stage, lr, ad, jump);
try_locking(stage, lr, ad, jump);
apply_gravity(stage);
project_position(stage);
move_ship(stage);
check_horizontal_eject(stage, noise);
check_collisions(stage, noise);
check_scratching(stage);
clear_flag(flag_landed);
check_landing(stage);
if(vpos < 0) vpos = 0;
use_suppiles(stage);
check_death(stage);
framecounter++;
return 0;
}
void physics::level_init(level& stage)
{
gravity = stage.get_gravity();
o2_amount = stage.get_o2_amount();
fuel_amount = stage.get_fuel_amount();
gravity_accel = -((72 * static_cast<int32_t>(gravity) / 5) & 0xFFFF);
bounce_limit = (260 * static_cast<int32_t>(gravity) / 8) & 0xFFFF;
if(o2_amount)
o2_factor = 30000 / ((36 * (int32_t)o2_amount) & 0xFFFF);
else
o2_factor = 30000;
if(fuel_amount)
fuel_factor = 30000 / fuel_amount;
else
fuel_factor = 65535;
framecounter = 0;
deathframe = 0;
lpos = 3 << 16;
lprojected = 1999185; //Crap from memory.
lspeed = 0;
speedbias = 0;
expframe = 0;
postdeath = 0;
hpos = 32768;
vpos = 10240;
hprojected = 47370; //Crap from memory.
vprojected = 0; //Crap from memory.
hspeed = 0;
vspeed = 0;
hdrift = 0;
jump_ground = 10240;
nosupport = 19422; //Crap from memory.
nosupport_d = 16199; //Crap from memory.
jump_ground = 0;
fuel_left = 30000;
o2_left = 30000;
death = 0;
flags = flag_landed | ((gravity >= 20) ? flag_no_jump : 0);
}
}

View file

@ -0,0 +1,103 @@
#ifndef _skycore__physics__hpp__included__
#define _skycore__physics__hpp__included__
#include <cstdint>
#include <iostream>
#include <cstring>
#include "level.hpp"
namespace sky
{
struct noise_maker
{
virtual ~noise_maker();
virtual void operator()(int sound, bool hipri = true) = 0;
};
struct physics
{
const static uint8_t death_none = 0; //Playing.
const static uint8_t death_burning = 1; //Hit burning floor.
const static uint8_t death_drifting = 2; //Drifting forever in space.
const static uint8_t death_fuel = 3; //Ran out of fuel.
const static uint8_t death_o2 = 4; //Suffocated.
const static uint8_t death_collided = 5; //Ram wall too fast.
const static uint8_t death_escaped = 6; //Escaped level.
const static uint8_t death_finished = 255; //Exited level (finished).
const static uint8_t flag_locked = 1; //In locked jump.
const static uint8_t flag_tried_lock = 2; //Tried to lock this jump.
const static uint8_t flag_jumping = 4; //Currently in jump.
const static uint8_t flag_landed = 8; //Currently landed.
const static uint8_t flag_slippery = 16; //Slippery effect.
const static uint8_t flag_sticky = 32; //Sticky effect.
const static uint8_t flag_blank = 64; //Tile blank.
const static uint8_t flag_no_jump = 128; //Disable jumping.
//This class MUST NOT contain pointers and MUST be prepared to deal with any values!
uint32_t framecounter; //Frame counter.
uint32_t deathframe; //Frame of death.
uint32_t lpos; //Longitudial position.
uint32_t lprojected; //Longitudial projected position.
int32_t lspeed; //Longitudial speed.
int32_t speedbias; //Temporary jump speed boost
uint16_t expframe; //Explosion frame (0 if not exploded).
uint16_t postdeath; //Number of frames after death.
int16_t gravity_accel; //Acceleration of gravity (negative)
uint16_t hpos; //Horizontal position.
int16_t vpos; //Vertical position.
uint16_t hprojected; //Horizontal projected position.
int16_t vprojected; //Vertical projected position.
int16_t hspeed; //Horizontal speed.
int16_t vspeed; //Vertical speed.
int16_t hdrift; //Speed of horizontal drift.
int16_t jump_ground; //Jumping ground level.
int16_t nosupport; //Amount of lack of support.
int16_t nosupport_d; //Balance of lack of support.
uint16_t fuel_left; //Amount of fuel left.
uint16_t o2_left; //Amount of O2 left.
int16_t gravity; //Level gravity.
uint16_t o2_factor; //O2 use factor.
uint16_t fuel_factor; //Fuel use factor.
uint16_t bounce_limit; //Minimum speed ship bounces on.
int16_t o2_amount; //Level amount of O2.
uint16_t fuel_amount; //Level amount of fuel
uint8_t death; //Cause of death (0 => Not dead)
uint8_t flags; //Flags.
//Padding to multiple of 8 bytes.
uint8_t padA;
uint8_t padB;
uint8_t padC;
uint8_t padD;
void level_init(level& stage);
uint8_t simulate_frame(level& stage, noise_maker& noise, int lr, int ad, bool jump);
bool is_set(uint8_t b);
private:
void adjust_speed(level& stage, int adjust) throw();
void die(level& stage, uint8_t cause);
void explode(level& stage, noise_maker& noise);
void apply_floor_effects(level& stage, noise_maker& noise, unsigned floor);
void check_exit(level& stage) throw();
void use_suppiles(level& stage) throw();
void check_death(level& stage) throw();
void apply_steering(level& stage, int lr, int ad, bool jump) throw();
void apply_gravity(level& stage) throw();
void project_position(level& stage) throw();
uint8_t get_death(level& stage) throw();
void check_scratching(level& stage) throw();
void check_collisions(level& stage, noise_maker& noise) throw();
void try_locking(level& stage, int lr, int ad, bool jump) throw();
void check_horizontal_eject(level& stage, noise_maker& noise) throw();
void apply_bounce(level& stage, noise_maker& noise) throw();
void check_landing(level& stage) throw();
void move_ship(level& stage) throw();
bool dangerous_jump(level& stage, int ad, uint32_t lp, uint16_t hp, int16_t vp, int32_t lv,
int16_t hv, int16_t vv, int16_t hd);
void force_flag(uint8_t b, bool s);
void set_flag(uint8_t b);
void clear_flag(uint8_t b);
bool is_masked(uint8_t b, uint8_t c);
bool is_any_of(uint8_t b);
bool is_clear(uint8_t b);
};
}
#endif

View file

@ -0,0 +1,233 @@
#include "romimage.hpp"
#include "framebuffer.hpp"
#include "tasdemos.hpp"
#include "physics.hpp"
#include "library/bintohex.hpp"
#include "library/string.hpp"
#include "library/zip.hpp"
#include <sys/time.h>
#include <boost/iostreams/categories.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/iostreams/filter/symmetric.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
namespace sky
{
uint64_t get_utime()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
}
std::string rom_filename;
gauge speed_dat;
gauge oxydisp_dat;
gauge fueldisp_dat;
roads_lzs levels;
image ship;
image dashboard;
image levelselect;
image backgrounds[10];
sounds soundfx;
demo builtin_demo;
uint32_t dashpalette[16];
struct demoset_entry
{
uint8_t hash[32];
std::vector<char> demodata;
};
std::vector<demoset_entry> demos;
void load_builtin_demos(std::vector<demoset_entry>& _demos)
{
const unsigned char* ptr = tasdemos_data;
while(*ptr) {
ptr++;
demoset_entry e;
memcpy(e.hash, ptr, 32);
ptr += 32;
uint32_t l = 0;
l = (l << 8) | *(ptr++);
l = (l << 8) | *(ptr++);
l = (l << 8) | *(ptr++);
e.demodata.resize(l);
memcpy(&e.demodata[0], ptr, l);
ptr += l;
_demos.push_back(e);
}
}
void load_demos(std::vector<demoset_entry>& _demos, const std::string& filename)
{
zip_reader r(filename);
for(auto i : r) {
regex_results rx;
if(rx = regex("([0-9A-Fa-f]{64}).rec", i)) {
demoset_entry e;
memset(e.hash, 0, 32);
for(unsigned j = 0; j < 64; j++) {
unsigned x = i[j];
x = (x & 0x1F) ^ 0x10;
x = x - (x >> 4) * 7;
e.hash[j / 2] = 16 * e.hash[j / 2] + x;
}
std::istream& z = r[i];
boost::iostreams::back_insert_device<std::vector<char>> rd(e.demodata);
boost::iostreams::copy(z, rd);
delete &z;
_demos.push_back(e);
}
}
}
void load_rom(const std::string& filename)
{
std::string errfile;
try {
errfile = "/SPEED.DAT";
gauge _speed_dat(read_file_relative(filename + errfile, ""), 0x22);
errfile = "/OXY_DISP.DAT";
gauge _oxydisp_dat(read_file_relative(filename + errfile, ""), 0x0a);
errfile = "/FUL_DISP.DAT";
gauge _fueldisp_dat(read_file_relative(filename + errfile, ""), 0x0a);
errfile = "/ROADS.LZS";
roads_lzs _levels(read_file_relative(filename + errfile, ""));
errfile = "/CARS.LZS";
image _ship(read_file_relative(filename + errfile, ""));
errfile = "/DASHBRD.LZS";
image _dashboard(read_file_relative(filename + errfile, ""));
if(_dashboard.width != 320 || _dashboard.height > 200) {
std::cerr << _dashboard.width << "x" << _dashboard.height << std::endl;
throw std::runtime_error("Must be 320 wide and at most 200 high");
}
errfile = "/GOMENU.LZS";
image _levelselect(read_file_relative(filename + errfile, ""));
if(_levelselect.width != 320 || _levelselect.height != 200)
throw std::runtime_error("Must be 320x200");
errfile = "/SFX.SND";
sounds _soundfx(read_file_relative(filename + errfile, ""), 5);
errfile = "/DEMO.REC";
demo _builtin_demo(read_file_relative(filename + errfile, ""), true);
image _backgrounds[10];
for(unsigned i = 0; i < 10; i++) {
std::string n = "/WORLDx.LZS";
n[6] = '0' + i;
errfile = n;
//Skip nonexistent backgrounds.
try {
std::istream& x = open_file_relative(filename + errfile, "");
delete &x;
} catch(...) {
continue;
}
_backgrounds[i] = image(read_file_relative(filename + errfile, ""));
if(_backgrounds[i].width != 320 || _backgrounds[i].height > 200)
throw std::runtime_error("Must be 320 wide and at most 200 high");
}
std::vector<demoset_entry> _demos;
load_builtin_demos(_demos);
errfile = "<demos>";
load_demos(_demos, filename);
speed_dat = _speed_dat;
oxydisp_dat = _oxydisp_dat;
fueldisp_dat = _fueldisp_dat;
levels = _levels;
ship = _ship;
dashboard = _dashboard;
levelselect = _levelselect;
soundfx = _soundfx;
builtin_demo = _builtin_demo;
demos = _demos;
memcpy(dashpalette, dashboard.palette, sizeof(dashpalette));
for(unsigned i = 0; i < 10; i++)
backgrounds[i] = _backgrounds[i];
rom_filename = filename;
} catch(std::exception& e) { throw std::runtime_error(errfile + ": " + e.what()); }
}
//Combine background and dashboard into origbuffer and render.
void combine_background(size_t back)
{
memset(origbuffer, 0, sizeof(origbuffer));
image& bg = backgrounds[back];
if(bg.width && bg.height) {
size_t pixels = 320 * bg.height;
for(unsigned i = 0; i < pixels; i++)
origbuffer[i] = bg[i] & 0x00FFFFFFU;
}
{
size_t pixels = 320 * dashboard.height;
size_t writestart = 64000 - 320 * dashboard.height;
for(unsigned i = 0; i < pixels; i++)
if(dashboard.decode[i])
origbuffer[i + writestart] = dashboard[i] | 0xFF000000U;
}
render_backbuffer();
}
demo lookup_demo(const uint8_t* levelhash)
{
for(auto i = demos.rbegin(); i != demos.rend(); i++) {
if(!memcmp(i->hash, levelhash, 32))
return demo(i->demodata, false);
}
throw std::runtime_error("No demo found for level");
}
}
#ifdef DEMO_PLAYER
int main(int argc, char** argv)
{
sky::load_rom(argv[1]);
int lvl = atoi(argv[2]);
if(!sky::levels.present(lvl)) {
std::cerr << "Level " << lvl << " not found" << std::endl;
return 1;
}
sky::level& l = sky::levels[lvl];
uint8_t hash[32];
l.sha256_hash(hash);
std::cerr << "Level hash is " << binary_to_hex(hash, 32) << std::endl;
sky::demo d;
try {
if(lvl)
d = sky::lookup_demo(hash);
else
d = sky::builtin_demo;
} catch(std::exception& e) {
std::cerr << "Demo lookup failed" << std::endl;
return 1;
}
std::cerr << "Found a demo." << std::endl;
sky::physics p;
p.level_init(l);
uint64_t t1 = sky::get_utime();
while(!p.death) {
uint16_t buttons = d.fetchkeys(0, p.lpos, p.framecounter);
int lr = 0, ad = 0;
bool jump = ((buttons & 16) != 0);
if((buttons & 1) != 0) lr--;
if((buttons & 2) != 0) lr++;
if((buttons & 4) != 0) ad++;
if((buttons & 8) != 0) ad--;
if((buttons & 256) != 0) lr = 2; //Cheat for demo.
if((buttons & 512) != 0) ad = 2; //Cheat for demo.
p.simulate_frame(l, lr, ad, jump);
//std::cerr << p.framecounter << " pos: l=" << p.lpos << " h=" << p.hpos
// << " v=" << p.vpos << " death=" << (int)p.death << std::endl;
}
uint64_t t2 = sky::get_utime();
std::cerr << "Simulated " << p.framecounter << " frames in " << (t2 - t1) << "usec ("
<< 1000000 * p.framecounter / (t2 - t1) << " fps)" << std::endl;
if(p.death == 255)
std::cerr << "LEVEL COMPLETED!" << std::endl;
return (p.death == 255) ? 0 : 2;
}
#endif

View file

@ -0,0 +1,35 @@
#ifndef _skycore__romimage__hpp__included__
#define _skycore__romimage__hpp__included__
#include <cstdint>
#include <iostream>
#include <cstring>
#include <stdexcept>
#include <vector>
#include "gauge.hpp"
#include "level.hpp"
#include "image.hpp"
#include "sound.hpp"
#include "demo.hpp"
namespace sky
{
extern std::string rom_filename;
extern gauge speed_dat;
extern gauge oxydisp_dat;
extern gauge fueldisp_dat;
extern roads_lzs levels;
extern image ship;
extern image dashboard;
extern image levelselect;
extern image backgrounds[10];
extern sounds soundfx;
extern demo builtin_demo;
extern uint32_t dashpalette[16];
void load_rom(const std::string& filename);
void combine_background(size_t back);
demo lookup_demo(const uint8_t* levelhash);
}
#endif

404
src/emulation/sky/sky.cpp Normal file
View file

@ -0,0 +1,404 @@
#include "state.hpp"
#include "romimage.hpp"
#include "framebuffer.hpp"
#include "logic.hpp"
#include "demo.hpp"
#include "core/audioapi.hpp"
#include "core/window.hpp"
#include "interface/romtype.hpp"
#include "interface/callbacks.hpp"
#include "library/pixfmt-rgb32.hpp"
namespace sky
{
bool pflag;
int cstyle = 0;
const unsigned iindexes[3][7] = {
{0, 1, 2, 3, 4, 5, 6},
{6, 7, 4, 5, 8, 3, 2},
{5, 4, 6, 7, 0, 3, 2}
};
//Framebuffer.
uint32_t cover_fbmem[320*200];
struct framebuffer_info cover_fbinfo = {
&_pixel_format_rgb32, //Format.
(char*)cover_fbmem, //Memory.
320, 200, 1280, //Physical size.
320, 200, 1280, //Logical size.
0, 0 //Offset.
};
port_controller_button X5 = {port_controller_button::TYPE_BUTTON, 'F', "framesync", true};
port_controller_button* X3[] = {&X5};
port_controller X4 = {"(system)", "(system)", 1, X3};
port_controller_button X9 = {port_controller_button::TYPE_BUTTON, 'L', "left", true};
port_controller_button X10 = {port_controller_button::TYPE_BUTTON, 'R', "right", true};
port_controller_button X11 = {port_controller_button::TYPE_BUTTON, 'A', "up", true};
port_controller_button X12 = {port_controller_button::TYPE_BUTTON, 'D', "down", true};
port_controller_button X13 = {port_controller_button::TYPE_BUTTON, 'J', "A", true};
port_controller_button X14 = {port_controller_button::TYPE_BUTTON, 'S', "start", true};
port_controller_button X15 = {port_controller_button::TYPE_BUTTON, 's', "select", true};
port_controller_button X16 = {port_controller_button::TYPE_NULL, '\0', "", true};
port_controller_button X17 = {port_controller_button::TYPE_NULL, '\0', "", true};
port_controller_button* X7[] = {&X9,&X10,&X11,&X12,&X13,&X14,&X15,&X16,&X17};
port_controller X8 = {"sky", "sky", 7, X7};
port_controller* X1[] = {&X4,&X8};
port_controller_set X2 = {2, X1};
void port_write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x)
{
switch(idx) {
case 0:
switch(ctrl) {
case 0: if(x) buffer[0] |= 1; else buffer[0] &= ~1; break;
};
break;
case 1:
switch(256 * cstyle + ctrl) {
case 0: if(x) buffer[0] |= 2; else buffer[0] &= ~2; break;
case 1: if(x) buffer[0] |= 4; else buffer[0] &= ~4; break;
case 2: if(x) buffer[0] |= 8; else buffer[0] &= ~8; break;
case 3: if(x) buffer[0] |= 16; else buffer[0] &= ~16; break;
case 4: if(x) buffer[0] |= 32; else buffer[0] &= ~32; break;
case 5: if(x) buffer[0] |= 64; else buffer[0] &= ~64; break;
case 6: if(x) buffer[0] |= 128; else buffer[0] &= ~128; break;
case 258: if(x) buffer[0] |= 128; else buffer[0] &= ~128; break;
case 259: if(x) buffer[0] |= 64; else buffer[0] &= ~64; break;
case 260: if(x) buffer[0] |= 8; else buffer[0] &= ~8; break;
case 261: if(x) buffer[0] |= 16; else buffer[0] &= ~16; break;
case 262: if(x) buffer[0] |= 2; else buffer[0] &= ~2; break;
case 263: if(x) buffer[0] |= 4; else buffer[0] &= ~4; break;
case 264: if(x) buffer[0] |= 32; else buffer[0] &= ~32; break;
case 512: if(x) buffer[0] |= 32; else buffer[0] &= ~32; break;
case 514: if(x) buffer[0] |= 128; else buffer[0] &= ~128; break;
case 515: if(x) buffer[0] |= 64; else buffer[0] &= ~64; break;
case 516: if(x) buffer[0] |= 4; else buffer[0] &= ~4; break;
case 517: if(x) buffer[0] |= 2; else buffer[0] &= ~2; break;
case 518: if(x) buffer[0] |= 8; else buffer[0] &= ~8; break;
case 519: if(x) buffer[0] |= 16; else buffer[0] &= ~16; break;
};
};
}
short port_read(const unsigned char* buffer, unsigned idx, unsigned ctrl)
{
switch(idx) {
case 0:
switch(ctrl) {
case 0: return (buffer[0] & 1) ? 1 : 0;
}
break;
case 1:
switch(256 * cstyle + ctrl) {
case 0: return (buffer[0] & 2) ? 1 : 0;
case 1: return (buffer[0] & 4) ? 1 : 0;
case 2: return (buffer[0] & 8) ? 1 : 0;
case 3: return (buffer[0] & 16) ? 1 : 0;
case 4: return (buffer[0] & 32) ? 1 : 0;
case 5: return (buffer[0] & 64) ? 1 : 0;
case 6: return (buffer[0] & 128) ? 1 : 0;
case 258: return (buffer[0] & 128) ? 1 : 0;
case 259: return (buffer[0] & 64) ? 1 : 0;
case 260: return (buffer[0] & 8) ? 1 : 0;
case 261: return (buffer[0] & 16) ? 1 : 0;
case 262: return (buffer[0] & 2) ? 1 : 0;
case 263: return (buffer[0] & 4) ? 1 : 0;
case 264: return (buffer[0] & 32) ? 1 : 0;
case 512: return (buffer[0] & 32) ? 1 : 0;
case 514: return (buffer[0] & 128) ? 1 : 0;
case 515: return (buffer[0] & 64) ? 1 : 0;
case 516: return (buffer[0] & 4) ? 1 : 0;
case 517: return (buffer[0] & 2) ? 1 : 0;
case 518: return (buffer[0] & 8) ? 1 : 0;
case 519: return (buffer[0] & 16) ? 1 : 0;
};
break;
};
return 0;
}
void port_display(const unsigned char* buffer, unsigned idx, char* buf)
{
size_t ptr = 0;
short tmp;
switch(idx) {
case 0:
buf[ptr++] = (buffer[0] & 1) ? 'F' : '-';
break;
case 1:
buf[ptr++] = (buffer[0] & 2) ? 'L' : '-';
buf[ptr++] = (buffer[0] & 4) ? 'R' : '-';
buf[ptr++] = (buffer[0] & 8) ? 'A' : '-';
buf[ptr++] = (buffer[0] & 16) ? 'D' : '-';
buf[ptr++] = (buffer[0] & 32) ? 'J' : '-';
buf[ptr++] = (buffer[0] & 64) ? 'S' : '-';
buf[ptr++] = (buffer[0] & 128) ? 's' : '-';
break;
};
buf[ptr] = '\0';
};
size_t port_serialize(const unsigned char* buffer, char* textbuf)
{
size_t ptr = 0;
short tmp;
textbuf[ptr++] = (buffer[0] & 1) ? 'F' : '.';
textbuf[ptr++] = '|';
textbuf[ptr++] = (buffer[0] & 2) ? 'L' : '.';
textbuf[ptr++] = (buffer[0] & 4) ? 'R' : '.';
textbuf[ptr++] = (buffer[0] & 8) ? 'A' : '.';
textbuf[ptr++] = (buffer[0] & 16) ? 'D' : '.';
textbuf[ptr++] = (buffer[0] & 32) ? 'J' : '.';
textbuf[ptr++] = (buffer[0] & 64) ? 'S' : '.';
textbuf[ptr++] = (buffer[0] & 128) ? 's' : '.';
textbuf[ptr] = '\0';
return ptr;
};
size_t port_deserialize(unsigned char* buffer, const char* textbuf)
{
memset(buffer, 0, 2);
size_t ptr = 0;
short tmp;
if(read_button_value(textbuf, ptr)) buffer[0] |= 1;
skip_rest_of_field(textbuf, ptr, true);
if(read_button_value(textbuf, ptr)) buffer[0] |= 2;
if(read_button_value(textbuf, ptr)) buffer[0] |= 4;
if(read_button_value(textbuf, ptr)) buffer[0] |= 8;
if(read_button_value(textbuf, ptr)) buffer[0] |= 16;
if(read_button_value(textbuf, ptr)) buffer[0] |= 32;
if(read_button_value(textbuf, ptr)) buffer[0] |= 64;
if(read_button_value(textbuf, ptr)) buffer[0] |= 128;
skip_rest_of_field(textbuf, ptr, false);
};
int port_legal(unsigned c)
{
if(c == 0) return true;
return false;
};
unsigned port_used_indices(unsigned c)
{
if(c == 0) return 1;
if(c == 1) return 9;
return 0;
};
struct _psystem : public port_type
{
_psystem() : port_type("system", "system", 1,1)
{
write = port_write;
read = port_read;
display = port_display;
serialize = port_serialize;
deserialize = port_deserialize;
legal = port_legal;
used_indices = port_used_indices;
controller_info = &X2;
}
} psystem;
port_type* port_types[] = {&psystem, NULL};
port_index_triple t(unsigned p, unsigned c, unsigned i, bool nl)
{
port_index_triple x;
x.valid = true;
x.port = p;
x.controller = c;
x.control = i;
return x;
}
struct core_setting_group sky_settings;
unsigned world_compat[2] = {0, UINT_MAX};
struct core_region_params world_region_params = {
"world", "World", 0, 0, false, {656250, 18227}, world_compat
};
struct core_region world_region(world_region_params);
struct core_region* regions[] = {&world_region, NULL};
struct core_romimage_info_params skyzip_params = {
"rom", "skyroads.zip", 1, 1, 0
};
struct core_romimage_info skyzip(skyzip_params);
struct core_romimage_info* images[] = {&skyzip, NULL};
extern struct core_core sky_core;
struct core_core_params sky_core_params = {
[]() -> std::string { return "Sky"; },
[](core_region& region) -> bool { return (&region == &world_region); },
[]() -> std::pair<uint32_t, uint32_t> { return std::make_pair(656250, 18227); },
[]() -> std::pair<uint32_t, uint32_t> { return std::make_pair(48000, 1); },
[]() -> std::pair<uint32_t, uint32_t> { return std::make_pair(0, 0); },
[]() -> std::map<std::string, std::vector<char>> {
std::map<std::string, std::vector<char>> r;
std::vector<char> sram;
sram.resize(32);
memcpy(&sram[0], _gstate.sram, 32);
r["sram"] = sram;
return r;
},
[](std::map<std::string, std::vector<char>>& sram) -> void {
if(sram.count("sram") && sram["sram"].size() == 32)
memcpy(_gstate.sram, &sram["sram"][0], 32);
else
memset(_gstate.sram, 0, 32);
},
[](std::vector<char>& out) -> void {
auto wram = _gstate.as_ram();
out.resize(wram.second);
memcpy(&out[0], wram.first, wram.second);
},
[](const char* in, size_t insize) -> void {
auto wram = _gstate.as_ram();
if(insize != wram.second)
throw std::runtime_error("Save is of wrong size");
memcpy(wram.first, in, wram.second);
handle_loadstate(_gstate);
},
[]() -> core_region& { return world_region; },
[]() -> void {},
[]() -> void {},
[](uint32_t w, uint32_t h) -> std::pair<uint32_t, uint32_t> {
return std::make_pair(FB_WIDTH / w, FB_HEIGHT / h);
},
[]() -> void { sky_core.hide(); },
[]() -> void {},
[]() -> void {
static unsigned count[4];
static unsigned tcount[4] = {5, 7, 8, 25};
uint16_t x = 0;
for(unsigned i = 0; i < 7; i++)
if(ecore_callbacks->get_input(0, 1, iindexes[cstyle][i]))
x |= (1 << i);
pflag = true;
simulate_frame(_gstate, x);
uint32_t* fb = indirect_flag ? fadeffect_buffer : sky::framebuffer;
framebuffer_info inf;
inf.type = &_pixel_format_rgb32;
inf.mem = const_cast<char*>(reinterpret_cast<const char*>(fb));
inf.physwidth = FB_WIDTH;
inf.physheight = FB_HEIGHT;
inf.physstride = 4 * FB_WIDTH;
inf.width = FB_WIDTH;
inf.height = FB_HEIGHT;
inf.stride = 4 * FB_WIDTH;
inf.offset_x = 0;
inf.offset_y = 0;
framebuffer_raw ls(inf);
ecore_callbacks->output_frame(ls, 656250, 18227);
ecore_callbacks->timer_tick(18227, 656250);
size_t samples = 1333;
size_t extrasample = 0;
for(unsigned i = 0; i < 4; i++) {
count[i]++;
if(count[i] == tcount[i]) {
count[i] = 0;
extrasample = extrasample ? 0 : 1;
} else
break;
}
samples += extrasample;
int16_t sbuf[2668];
fetch_sfx(_gstate, sbuf, samples);
audioapi_submit_buffer(sbuf, samples, true, 48000);
},
[]() -> void {},
[]() -> bool { return pflag; },
[](bool _pflag) -> void { pflag = _pflag; },
[](long delay, bool hard) -> void {},
port_types,
[]() -> framebuffer_raw& {
static framebuffer_raw x(cover_fbinfo);
return x;
},
[]() -> std::string { return "sky"; }
};
struct core_core sky_core(sky_core_params);
void controller_magic()
{
if(magic_flags & 1) {
X8.cclass = "gamepad";
X8.button_count = 9;
cstyle = 1;
} else if(magic_flags & 2) {
X8.cclass = "gb";
X8.button_count = 8;
cstyle = 2;
} else if(magic_flags & 4) {
X8.cclass = "gba";
X8.button_count = 8;
cstyle = 2;
} else {
cstyle = 0;
}
}
struct core_type_params skytype_params = {
"sky", "Sky", 3522, 0,
[](core_romimage* images, std::map<std::string, std::string>& settings, uint64_t rtc_sec,
uint64_t rtc_subsec) -> int {
controller_magic();
const unsigned char* _filename = images[0].data;
size_t size = images[0].size;
std::string filename(_filename, _filename + size);
try {
load_rom(filename);
} catch(std::exception& e) {
messages << e.what();
return -1;
}
rom_boot_vector(_gstate);
return 0;
},
[](std::map<std::string, std::string>& settings) -> controller_set
{
controller_magic();
controller_set r;
r.ports.push_back(&psystem);
r.portindex.indices.push_back(t(0, 0, 0, false));
for(unsigned i = 0; i < 9; i++)
r.portindex.indices.push_back(t(0, 1, i, true));
r.portindex.logical_map.push_back(std::make_pair(0, 1));
r.portindex.pcid_map.push_back(std::make_pair(0, 1));
return r;
},
"sky",
NULL,
regions,
images,
&sky_settings,
&sky_core,
[]() -> std::pair<uint64_t, uint64_t> { return std::make_pair(0, 0); },
[]() -> std::list<core_vma_info> {
std::list<core_vma_info> r;
core_vma_info ram;
ram.name = "RAM";
ram.backing_ram = _gstate.as_ram().first;
ram.size = _gstate.as_ram().second;
ram.base = 0;
ram.readonly = false;
ram.endian = 0;
ram.iospace_rw = NULL;
r.push_back(ram);
return r;
},
[]() -> std::set<std::string> {
std::set<std::string> r;
r.insert("sram");
return r;
}
};
struct core_type skytype(skytype_params);
core_sysregion X24("sky", skytype, world_region);
}

121
src/emulation/sky/sound.cpp Normal file
View file

@ -0,0 +1,121 @@
#include "sound.hpp"
#include "romimage.hpp"
#include "util.hpp"
#include "state.hpp"
#include "library/string.hpp"
#include "library/zip.hpp"
#include "library/minmax.hpp"
namespace sky
{
sound::sound()
{
snds = NULL;
rate = 128;
pointer = 0;
length = 0;
}
sound::sound(struct sounds& _snds, uint8_t _rate, uint32_t ptr, uint32_t len)
{
snds = &_snds;
rate = _rate;
pointer = ptr;
length = len;
}
sounds::sounds()
{
}
sounds::sounds(const std::vector<char>& snd, size_t samples)
{
sounddata = snd;
sfx.resize(samples);
if(snd.size() < 2 * samples + 2)
(stringfmt() << "Sound pointer table incomplete").throwex();
for(unsigned i = 0; i < samples; i++) {
size_t sptr = combine(snd[2 * i + 0], snd[2 * i + 1]);
size_t eptr = combine(snd[2 * i + 2], snd[2 * i + 3]);
if(sptr >= snd.size())
(stringfmt() << "Sound " << i << " points outside file").throwex();
if(eptr > snd.size())
(stringfmt() << "Sound " << i << " extends past the end of file").throwex();
if(eptr <= sptr)
(stringfmt() << "Sound " << i << " size invalid (must be >0)").throwex();
uint8_t rate = snd[sptr];
sfx[i] = sound(*this, rate, sptr + 1, eptr - sptr - 1);
}
}
active_sfx_dma::active_sfx_dma()
{
//End of transfer.
left = 0;
pointer = 0;
subsample = 0;
mdr = 128;
rate = 128;
}
void active_sfx_dma::reset(const struct sound& snd)
{
rate = snd.get_rate();
left = 48000ULL * (256 - rate) * snd.get_length();
subsample = 0;
pointer = snd.get_pointer();
mdr = access(snd.get_sounds(), pointer++);
}
void active_sfx_dma::fetch(struct sounds& snds, int16_t* buffer, size_t samples)
{
while(left > 0 && samples > 0) {
*(buffer++) = 256 * ((int16_t)mdr - 128);
*(buffer++) = 256 * ((int16_t)mdr - 128);
subsample += 1000000;
while(subsample > 48000ULL * (256 - rate)) {
mdr = snds.access(pointer++);
subsample -= 48000ULL * (256 - rate);
}
left -= 1000000;
samples--;
}
//Fill the rest with silence.
for(size_t i = 0; i < samples; i++) {
*(buffer++) = 0;
*(buffer++) = 0;
}
}
void fetch_sfx(gstate& s, int16_t* buffer, size_t samples)
{
static std::vector<std::pair<int16_t, int16_t>> buf;
if(buf.size() < samples)
buf.resize(samples);
s.dma.fetch(soundfx, buffer, samples);
try {
mplayer.decode(&buf[0], samples);
} catch(...) {
}
for(size_t i = 0; i < samples; i++) {
buffer[2 * i + 0] = max(min((int32_t)buffer[2 * i + 0] + buf[i].first, 32767), -32768);
buffer[2 * i + 1] = max(min((int32_t)buffer[2 * i + 1] + buf[i].second, 32767), -32768);
}
}
sound_noise_maker::sound_noise_maker(const sounds& _snds, struct active_sfx_dma& _dma)
: snds(_snds), dma(_dma)
{
}
sound_noise_maker::~sound_noise_maker() {}
void sound_noise_maker::operator()(int sound, bool hipri)
{
if(!hipri && dma.busy())
return;
dma.reset(snds[sound]);
}
sound_noise_maker gsfx(soundfx, _gstate.dma);
}

View file

@ -0,0 +1,80 @@
#ifndef _skycore__sound__hpp__included__
#define _skycore__sound__hpp__included__
#include <cstdint>
#include <iostream>
#include <cstring>
#include <stdexcept>
#include <vector>
#include "physics.hpp"
namespace sky
{
const int sound_explode = 0; //Ship explodes.
const int sound_bounce = 1; //Ship bounces from floor.
const int sound_blow = 2; //Ship takes a blow (not fatal).
const int sound_beep = 3; //O2/fuel exhausted.
const int sound_suppiles = 4; //Suppiles received.
struct sounds;
struct sound
{
sound();
sound(struct sounds& _snds, uint8_t _rate, uint32_t ptr, uint32_t len);
const sounds& get_sounds() const { return *snds; }
uint8_t get_rate() const { return rate; }
uint32_t get_pointer() const { return pointer; }
uint32_t get_length() const { return length; }
private:
sounds* snds;
uint8_t rate;
uint32_t pointer;
uint32_t length;
};
struct sounds
{
sounds();
sounds(const std::vector<char>& snd, size_t samples);
const sound& operator[](size_t idx) const { return (idx < sfx.size()) ? sfx[idx] : dummy; }
uint8_t access(size_t idx) const { return (idx < sounddata.size()) ? sounddata[idx] : 0x80; }
private:
std::vector<char> sounddata;
std::vector<sound> sfx;
sound dummy;
};
struct gstate;
void fetch_sfx(gstate& s, int16_t* buffer, size_t samples); //Stereo!
struct active_sfx_dma
{
active_sfx_dma();
void reset(const struct sound& snd);
void fetch(struct sounds& snds, int16_t* buffer, size_t samples); //Stereo!
bool busy() { return (left > 0); }
private:
uint8_t access(const struct sounds& snds, uint32_t addr) { snds.access(addr); }
int64_t left;
uint32_t pointer;
uint32_t subsample;
uint32_t padA;
uint16_t padB;
uint8_t mdr;
uint8_t rate;
};
struct sound_noise_maker : public noise_maker
{
sound_noise_maker(const sounds& _snds, struct active_sfx_dma& _dma);
~sound_noise_maker();
void operator()(int sound, bool hipri = true);
private:
const sounds& snds;
active_sfx_dma& dma;
};
extern sound_noise_maker gsfx;
}
#endif

View file

@ -0,0 +1,47 @@
#include "state.hpp"
#include "romimage.hpp"
namespace sky
{
const char* statenames[] = {
"MENU_FADEIN", "MENU", "MENU_FADEOUT", "LOAD_LEVEL", "LEVEL_FADEIN", "LEVEL_PLAY", "LEVEL_COMPLETE",
"LEVEL_FADEOUT", "LOAD_MENU", "LEVEL_UNAVAIL", "DEMO_UNAVAIL", "LEVEL_FADEOUT_RETRY"
};
void gstate::level_init(uint8_t _stage)
{
stage = _stage;
p.level_init(curlevel);
paused = 0;
speedind = 0;
fuelind = 0;
o2ind = 0;
distind = 0;
lockind = 0;
beep_phase = 0;
}
uint8_t gstate::simulate_frame(int lr, int ad, bool jump)
{
uint8_t dstatus = p.simulate_frame(curlevel, gsfx, lr, ad, jump);
uint16_t lt = p.lpos >> 16;
uint8_t ht = (p.hpos - 12160) / 5888;
if(secret == 1 && p.is_set(physics::flag_landed) && ht <= 2 && lt >= 131 && lt <= 170)
secret |= 0x80;
else if(secret == 2 && p.is_set(physics::flag_landed) && ht == 3 && lt >= 58 && lt <= 62)
secret |= 0x80;
return dstatus;
}
void gstate::change_state(uint8_t newstate)
{
state = newstate;
fadecount = 0;
}
std::pair<uint8_t*, size_t> gstate::as_ram()
{
return std::make_pair(reinterpret_cast<uint8_t*>(this), sizeof(*this));
}
gstate _gstate;
music_player mplayer(_gstate.pcmpos);
}

View file

@ -0,0 +1,70 @@
#ifndef _skycore__state__hpp__included__
#define _skycore__state__hpp__included__
#include <cstdlib>
#include <cstdint>
#include "physics.hpp"
#include "music.hpp"
#include "demo.hpp"
#include "level.hpp"
#include "sound.hpp"
namespace sky
{
const uint8_t state_menu_fadein = 0; //Menu fading in.
const uint8_t state_menu = 1; //In menu.
const uint8_t state_menu_fadeout = 2; //Menu fading out.
const uint8_t state_load_level = 3; //Level being loaded.
const uint8_t state_level_fadein = 4; //Level fading in.
const uint8_t state_level_play = 5; //Level being played.
const uint8_t state_level_complete = 6; //Level completed.
const uint8_t state_level_fadeout = 7; //Level fading out.
const uint8_t state_load_menu = 8; //Menu being loaded.
const uint8_t state_level_unavail = 9; //Level unavailable.
const uint8_t state_demo_unavail = 10; //Demo unavailable.
const uint8_t state_level_fadeout_retry = 11; //Level fading out for retry.
const uint8_t state_load_level_nomus = 12; //Level being loaded, without reloading music.
const uint8_t state_lockup = 13; //Game is locked up.
struct gstate
{
//DO NOT PUT POINTERS IN HERE!!!
//Also, be careful not to do anything undefined if there is a bad value.
demo curdemo;
level curlevel;
active_sfx_dma dma;
physics p;
uint64_t pcmpos; //PCM position in song.
uint64_t frames_ran; //Number of frames run.
uint32_t cursong; //Current song number.
uint16_t waited; //Menu wait.
uint8_t paused; //Paused flag.
uint8_t speedind; //Indicated speed.
uint8_t o2ind; //Indicated amount of oxygen.
uint8_t fuelind; //Indicated amount of fuel.
uint8_t distind; //Indicated distance.
uint8_t lockind; //Lock indicator flag.
uint8_t beep_phase; //Out of O2/Fuel flash phase.
uint8_t state; //State of game.
uint8_t fadecount; //Fade counter.
uint8_t stage; //Current stage.
uint8_t oldstage; //old stage (used in menu).
uint8_t savestage; //Saved stage (used over demo).
uint8_t demo_flag; //Set to 1 to load demo.
uint8_t lastkeys; //Last key state.
uint8_t secret; //Secret flag.
uint8_t padB; //Padding.
uint8_t padC; //Padding.
uint8_t padD; //Padding.
uint8_t sram[32]; //SRAM.
void level_init(uint8_t _stage);
uint8_t simulate_frame(int lr, int ad, bool jump);
void change_state(uint8_t newstate);
std::pair<uint8_t*, size_t> as_ram();
};
extern gstate _gstate;
extern music_player mplayer;
inline gstate& get_state() { return _gstate; }
}
#endif

View file

@ -0,0 +1,513 @@
#include "tasdemos.hpp"
namespace sky
{
const unsigned char tasdemos_data[8065] =
{
0x01, 0xde, 0x6a, 0x12, 0x51, 0xc9, 0x89, 0x26, 0xce, 0x98, 0x6c, 0xb2, 0x47, 0xe3, 0x99, 0xca,
0x9e, 0xdc, 0x27, 0xef, 0xc3, 0x02, 0x0b, 0x75, 0xc5, 0xa3, 0x68, 0x5d, 0x39, 0xf9, 0x04, 0xfd,
0xc9, 0x00, 0x00, 0x30, 0x00, 0x09, 0x17, 0x0a, 0x2b, 0x09, 0x0f, 0x1a, 0x1a, 0x08, 0x09, 0x09,
0x09, 0x18, 0x12, 0x0a, 0x04, 0x09, 0x04, 0x18, 0x08, 0x08, 0x16, 0x1a, 0x01, 0x0a, 0x1e, 0x09,
0x0b, 0x19, 0x20, 0x09, 0x02, 0x19, 0x09, 0x18, 0x01, 0x08, 0x07, 0x09, 0x03, 0x19, 0x09, 0x1a,
0x18, 0x08, 0x2e, 0x09, 0x01, 0x68, 0xd5, 0x41, 0xf9, 0xaf, 0x17, 0x6b, 0x21, 0x51, 0x7d, 0x3b,
0xf3, 0x36, 0x25, 0xb4, 0x78, 0x1b, 0xfc, 0x1c, 0x7b, 0x18, 0x1d, 0x76, 0x51, 0x9a, 0x26, 0xba,
0xe3, 0xc5, 0x27, 0xde, 0xaf, 0x00, 0x00, 0x80, 0x00, 0x09, 0x26, 0x08, 0x13, 0x09, 0x0e, 0x1a,
0x09, 0x08, 0x0e, 0x09, 0x0e, 0x19, 0x1d, 0x09, 0x0b, 0x19, 0x0a, 0x09, 0x01, 0x08, 0x10, 0x18,
0x02, 0x08, 0x08, 0x09, 0x12, 0x1a, 0x03, 0x0a, 0x02, 0x19, 0x03, 0x09, 0x16, 0x0a, 0x03, 0x19,
0x0a, 0x18, 0x02, 0x08, 0x06, 0x0a, 0x05, 0x09, 0x00, 0x0a, 0x12, 0x1a, 0x03, 0x08, 0x11, 0x09,
0x04, 0x19, 0x12, 0x09, 0x0e, 0x18, 0x14, 0x09, 0x0b, 0x18, 0x00, 0x19, 0x23, 0x0a, 0x1a, 0x09,
0x0b, 0x18, 0x18, 0x09, 0x01, 0x0a, 0x0c, 0x1a, 0x06, 0x09, 0x01, 0x08, 0x0f, 0x09, 0x06, 0x19,
0x14, 0x09, 0x0d, 0x18, 0x04, 0x08, 0x00, 0x09, 0x06, 0x0a, 0x09, 0x09, 0x02, 0x19, 0x05, 0x18,
0x12, 0x09, 0x0f, 0x1a, 0x07, 0x09, 0x0e, 0x19, 0x07, 0x09, 0x02, 0x0a, 0x09, 0x1a, 0x0b, 0x0a,
0x00, 0x09, 0x1a, 0x19, 0x05, 0x09, 0x00, 0x05, 0x01, 0xe3, 0x44, 0xa5, 0xc5, 0x61, 0x93, 0x6f,
0xf8, 0xa8, 0xe3, 0x66, 0x48, 0x82, 0xea, 0x06, 0xa5, 0x8e, 0x0f, 0xa2, 0xd7, 0x91, 0x58, 0x70,
0xf2, 0x24, 0xa8, 0xb3, 0xf1, 0xdc, 0x6f, 0x97, 0xf0, 0x00, 0x00, 0x3c, 0x1e, 0x09, 0x0d, 0x19,
0x28, 0x09, 0x0d, 0x19, 0x18, 0x09, 0x07, 0x19, 0x0a, 0x09, 0x01, 0x0a, 0x06, 0x09, 0x0a, 0x18,
0x01, 0x08, 0x09, 0x09, 0x0f, 0x1a, 0x06, 0x09, 0x08, 0x08, 0x0a, 0x19, 0x10, 0x09, 0x01, 0x19,
0x05, 0x1a, 0x0b, 0x09, 0x08, 0x08, 0x10, 0x18, 0x08, 0x08, 0x1f, 0x1a, 0x00, 0x19, 0x17, 0x09,
0x0f, 0x19, 0x0b, 0x09, 0x14, 0x19, 0x0b, 0x09, 0x01, 0xf3, 0x0a, 0x2e, 0x1c, 0xfb, 0x45, 0x48,
0x76, 0x71, 0x75, 0x83, 0x34, 0xff, 0x5f, 0x78, 0x08, 0x44, 0x5b, 0x92, 0xe7, 0xff, 0xc4, 0xdc,
0xf8, 0xd9, 0xcf, 0x3d, 0xf3, 0x5e, 0x96, 0xe2, 0x30, 0x00, 0x00, 0x48, 0x4e, 0x09, 0x02, 0x08,
0x15, 0x09, 0x0c, 0x1a, 0x0d, 0x0a, 0x04, 0x08, 0x11, 0x18, 0x1e, 0x09, 0x0d, 0x0a, 0x11, 0x09,
0x08, 0x19, 0x19, 0x08, 0x08, 0x19, 0x0d, 0x09, 0x0e, 0x1a, 0x0e, 0x08, 0x0f, 0x18, 0x09, 0x08,
0x0b, 0x1a, 0x07, 0x0a, 0x38, 0x09, 0x01, 0x19, 0x0a, 0x18, 0x00, 0x19, 0x15, 0x09, 0x1d, 0x1a,
0x2d, 0x09, 0x06, 0x08, 0x00, 0x09, 0x0c, 0x19, 0x06, 0x09, 0x15, 0x19, 0x05, 0x09, 0x12, 0x18,
0x03, 0x0a, 0x11, 0x09, 0x01, 0x87, 0xff, 0xbc, 0x7a, 0x79, 0x90, 0xf0, 0x30, 0xcc, 0x5a, 0xd4,
0xa2, 0xea, 0xae, 0xeb, 0xe4, 0x75, 0x69, 0x5e, 0x1c, 0x4d, 0x46, 0xe3, 0x09, 0x24, 0xd6, 0xff,
0x77, 0x6b, 0x31, 0xb4, 0x74, 0x00, 0x00, 0x74, 0x00, 0x09, 0x1f, 0x0a, 0x19, 0x1a, 0x06, 0x0a,
0x0e, 0x09, 0x05, 0x08, 0x2b, 0x18, 0x06, 0x19, 0x01, 0x09, 0x06, 0x0a, 0x13, 0x1a, 0x06, 0x09,
0x11, 0x19, 0x01, 0x09, 0x02, 0x0a, 0x06, 0x1a, 0x04, 0x08, 0x1a, 0x18, 0x01, 0x08, 0x0a, 0x18,
0x09, 0x08, 0x2c, 0x09, 0x0b, 0x0a, 0x11, 0x1a, 0x05, 0x0a, 0x0a, 0x19, 0x00, 0x1a, 0x09, 0x0a,
0x03, 0x09, 0x18, 0x18, 0x24, 0x09, 0x00, 0x08, 0x01, 0x09, 0x00, 0x08, 0x01, 0x09, 0x00, 0x08,
0x1e, 0x09, 0x00, 0x08, 0x35, 0x09, 0x02, 0x19, 0x09, 0x1a, 0x07, 0x0a, 0x0a, 0x1a, 0x0d, 0x09,
0x0e, 0x19, 0x08, 0x09, 0x11, 0x19, 0x04, 0x09, 0x0f, 0x19, 0x07, 0x09, 0x06, 0x19, 0x2b, 0x09,
0x08, 0x08, 0x11, 0x18, 0x06, 0x08, 0x13, 0x09, 0x05, 0x19, 0x3b, 0x09, 0x01, 0xf0, 0xbb, 0xb6,
0x28, 0xae, 0x78, 0x5b, 0x02, 0x3b, 0x0f, 0x97, 0x49, 0xe0, 0xfb, 0x8c, 0x20, 0xb2, 0x20, 0x6f,
0xce, 0xab, 0x64, 0x57, 0x5d, 0xcb, 0x32, 0x0b, 0xa6, 0x5a, 0xdd, 0x1c, 0x6b, 0x00, 0x00, 0x2e,
0x00, 0x09, 0x10, 0x0a, 0x10, 0x1a, 0x11, 0x0a, 0x38, 0x09, 0x05, 0x19, 0x19, 0x09, 0x10, 0x18,
0x00, 0x08, 0x07, 0x0a, 0x0d, 0x09, 0x06, 0x19, 0x13, 0x09, 0x08, 0x0a, 0x07, 0x09, 0x0d, 0x19,
0x16, 0x09, 0x11, 0x18, 0x02, 0x09, 0x0b, 0x1a, 0x0e, 0x09, 0x08, 0x08, 0x12, 0x09, 0x01, 0x05,
0x3b, 0x08, 0x54, 0x70, 0x7f, 0x14, 0x66, 0x74, 0xc6, 0x8e, 0x52, 0x59, 0x0b, 0xf6, 0x64, 0xac,
0x4b, 0x8a, 0x15, 0x5f, 0x8d, 0xd3, 0x9e, 0x38, 0x3e, 0x18, 0x0e, 0x71, 0x44, 0x04, 0x89, 0x00,
0x00, 0x34, 0x00, 0x09, 0x0e, 0x08, 0x15, 0x18, 0x05, 0x0a, 0x07, 0x09, 0x2a, 0x19, 0x03, 0x09,
0x02, 0x19, 0x08, 0x1a, 0x12, 0x09, 0x0d, 0x1a, 0x10, 0x08, 0x07, 0x09, 0x04, 0x19, 0x2e, 0x09,
0x11, 0x19, 0x03, 0x09, 0x13, 0x19, 0x2f, 0x09, 0x01, 0x08, 0x10, 0x18, 0x07, 0x08, 0x1a, 0x19,
0x0f, 0x09, 0x0e, 0x0a, 0x05, 0x09, 0x01, 0x92, 0x38, 0x72, 0xef, 0x83, 0x9a, 0x39, 0x89, 0xbf,
0x7c, 0x01, 0xe8, 0x04, 0xa7, 0xf7, 0xb2, 0x3c, 0x14, 0xab, 0x54, 0x03, 0x13, 0xba, 0x94, 0x6d,
0x5a, 0x67, 0x14, 0x07, 0xc6, 0x6b, 0x9e, 0x00, 0x00, 0x56, 0x00, 0x09, 0x03, 0x0a, 0x29, 0x09,
0x0e, 0x18, 0x18, 0x0a, 0x1e, 0x09, 0x02, 0x0a, 0x0e, 0x1a, 0x07, 0x0a, 0x22, 0x09, 0x06, 0x19,
0x19, 0x09, 0x10, 0x19, 0x0c, 0x09, 0x02, 0x19, 0x0a, 0x18, 0x1a, 0x09, 0x04, 0x08, 0x04, 0x09,
0x05, 0x19, 0x0d, 0x09, 0x11, 0x19, 0x08, 0x09, 0x0b, 0x18, 0x11, 0x09, 0x05, 0x0a, 0x0d, 0x1a,
0x07, 0x0a, 0x06, 0x1a, 0x0f, 0x09, 0x0c, 0x19, 0x06, 0x09, 0x1d, 0x08, 0x50, 0x09, 0x05, 0x19,
0x0a, 0x09, 0x0f, 0x1a, 0x12, 0x09, 0x02, 0x19, 0x09, 0x18, 0x02, 0x09, 0x01, 0x0a, 0x3e, 0x09,
0x01, 0x65, 0xa1, 0xa7, 0x73, 0x40, 0x61, 0x2a, 0x55, 0x82, 0x5a, 0x5c, 0xcb, 0xaa, 0x51, 0xbe,
0x56, 0xc2, 0x54, 0xb2, 0x2a, 0x2e, 0x67, 0xb9, 0x96, 0x2f, 0xf5, 0xb0, 0xba, 0xa3, 0x1c, 0x22,
0xbf, 0x00, 0x00, 0x34, 0x00, 0x09, 0x15, 0x0a, 0x37, 0x09, 0x20, 0x19, 0x17, 0x18, 0x11, 0x1a,
0x07, 0x0a, 0x1d, 0x18, 0x0f, 0x0a, 0x07, 0x09, 0x0a, 0x0a, 0x0f, 0x1a, 0x0b, 0x0a, 0x17, 0x09,
0x07, 0x08, 0x0e, 0x18, 0x12, 0x09, 0x0a, 0x19, 0x1a, 0x09, 0x11, 0x1a, 0x04, 0x0a, 0x06, 0x09,
0x19, 0x19, 0x0e, 0x09, 0x07, 0x08, 0x1b, 0x09, 0x01, 0xa4, 0xdb, 0xa8, 0xc9, 0x92, 0xec, 0x97,
0x1d, 0xbc, 0x95, 0xe6, 0x37, 0xa8, 0xef, 0x3c, 0x3f, 0x16, 0x46, 0xb4, 0xea, 0xcc, 0xd4, 0xa2,
0xcf, 0xef, 0xfe, 0x5e, 0x46, 0x68, 0x13, 0x4e, 0x02, 0x00, 0x00, 0x4c, 0x00, 0x09, 0x2e, 0x0a,
0x08, 0x09, 0x06, 0x19, 0x0d, 0x09, 0x25, 0x19, 0x12, 0x18, 0x06, 0x09, 0x10, 0x19, 0x09, 0x09,
0x0b, 0x19, 0x0b, 0x0a, 0x07, 0x09, 0x09, 0x19, 0x14, 0x1a, 0x0c, 0x09, 0x06, 0x08, 0x06, 0x18,
0x1b, 0x09, 0x03, 0x0a, 0x0c, 0x19, 0x0a, 0x09, 0x02, 0x0a, 0x10, 0x1a, 0x13, 0x18, 0x09, 0x08,
0x0d, 0x19, 0x21, 0x09, 0x10, 0x19, 0x04, 0x09, 0x05, 0x0a, 0x0c, 0x1a, 0x0e, 0x0a, 0x11, 0x09,
0x09, 0x19, 0x15, 0x08, 0x0b, 0x09, 0x00, 0x05, 0x01, 0xda, 0xa0, 0xc2, 0x11, 0xa8, 0x63, 0xa9,
0x6c, 0x0c, 0x1c, 0x07, 0x88, 0xaa, 0x72, 0x89, 0xb0, 0x6b, 0x5a, 0x98, 0x9d, 0xa2, 0x38, 0x4d,
0x8d, 0xb6, 0xd2, 0x5b, 0x33, 0x8f, 0xff, 0x98, 0xeb, 0x00, 0x00, 0x60, 0x15, 0x09, 0x1d, 0x19,
0x0c, 0x09, 0x29, 0x19, 0x05, 0x09, 0x12, 0x19, 0x2a, 0x09, 0x23, 0x18, 0x0a, 0x09, 0x06, 0x19,
0x01, 0x09, 0x32, 0x0a, 0x0a, 0x19, 0x34, 0x09, 0x21, 0x19, 0x11, 0x09, 0x0a, 0x0a, 0x06, 0x08,
0x20, 0x09, 0x26, 0x19, 0x05, 0x09, 0x1e, 0x19, 0x18, 0x08, 0x05, 0x09, 0x1c, 0x1a, 0x3a, 0x09,
0x20, 0x18, 0x06, 0x09, 0x01, 0x08, 0x2d, 0x09, 0x20, 0x1a, 0x03, 0x09, 0x24, 0x19, 0x23, 0x09,
0x26, 0x19, 0x05, 0x09, 0x18, 0x18, 0x1c, 0x09, 0x21, 0x1a, 0x16, 0x09, 0x23, 0x19, 0x15, 0x09,
0x26, 0x19, 0x10, 0x09, 0x18, 0x19, 0x06, 0x09, 0x1c, 0x08, 0x3f, 0x09, 0x01, 0x71, 0xb0, 0x5c,
0x04, 0xff, 0x84, 0x39, 0x35, 0x3f, 0x68, 0xd8, 0x55, 0xa7, 0x59, 0x75, 0xa0, 0xf8, 0x53, 0x70,
0x97, 0xf3, 0x1e, 0x99, 0x80, 0xf0, 0x0a, 0x4a, 0xc3, 0x17, 0x44, 0x87, 0xa7, 0x00, 0x00, 0x64,
0x00, 0x09, 0x10, 0x0a, 0x0f, 0x1a, 0x00, 0x19, 0x03, 0x09, 0x17, 0x08, 0x2e, 0x09, 0x03, 0x19,
0x4a, 0x09, 0x12, 0x18, 0x01, 0x08, 0x01, 0x09, 0x0a, 0x0a, 0x02, 0x09, 0x45, 0x19, 0x13, 0x1a,
0x12, 0x18, 0x0c, 0x08, 0x2e, 0x09, 0x07, 0x19, 0x1e, 0x09, 0x0f, 0x19, 0x05, 0x08, 0x4d, 0x09,
0x08, 0x0a, 0x1d, 0x09, 0x03, 0x19, 0x23, 0x09, 0x09, 0x19, 0x06, 0x09, 0x01, 0x0a, 0x12, 0x09,
0x10, 0x18, 0x03, 0x09, 0x04, 0x0a, 0x33, 0x09, 0x07, 0x0a, 0x28, 0x09, 0x06, 0x0a, 0x28, 0x09,
0x07, 0x0a, 0x40, 0x09, 0x1b, 0x18, 0x13, 0x08, 0x00, 0x0a, 0x15, 0x09, 0x03, 0x19, 0x06, 0x1a,
0x11, 0x0a, 0x23, 0x09, 0x01, 0xdb, 0x48, 0xdd, 0xed, 0xa2, 0xb6, 0x1a, 0x8d, 0xc7, 0x66, 0xad,
0x92, 0xc9, 0xa5, 0x4b, 0x40, 0xba, 0x50, 0x96, 0x0c, 0x2e, 0x87, 0xfa, 0x99, 0xdd, 0x45, 0x24,
0x32, 0x67, 0x32, 0xfc, 0x1a, 0x00, 0x00, 0x48, 0x00, 0x09, 0x1a, 0x1a, 0x0d, 0x0a, 0x09, 0x1a,
0x22, 0x09, 0x09, 0x08, 0x0f, 0x09, 0x0e, 0x18, 0x25, 0x09, 0x07, 0x19, 0x8a, 0x09, 0x09, 0x1a,
0x0c, 0x0a, 0x24, 0x09, 0x10, 0x18, 0x02, 0x09, 0x0e, 0x19, 0x06, 0x08, 0x40, 0x19, 0x1a, 0x09,
0x19, 0x19, 0x10, 0x0a, 0x0b, 0x1a, 0x00, 0x19, 0x0a, 0x18, 0x10, 0x19, 0x06, 0x1a, 0x1b, 0x09,
0x0f, 0x19, 0x07, 0x09, 0x10, 0x18, 0x01, 0x09, 0x02, 0x0a, 0x13, 0x09, 0x13, 0x19, 0x07, 0x09,
0x01, 0x78, 0x74, 0x87, 0xc8, 0x4b, 0xf4, 0xf4, 0x21, 0xb9, 0x2c, 0x5c, 0x99, 0xc5, 0xc6, 0x3b,
0x0f, 0xd6, 0x97, 0xab, 0xa5, 0xf1, 0x9d, 0x6e, 0x90, 0x0c, 0x40, 0x84, 0xf1, 0x9b, 0xda, 0xd2,
0xba, 0x00, 0x00, 0x9c, 0x00, 0x09, 0x14, 0x0a, 0x17, 0x08, 0x10, 0x0a, 0x0d, 0x08, 0x0b, 0x0a,
0x0a, 0x08, 0x09, 0x0a, 0x08, 0x08, 0x08, 0x0a, 0x21, 0x08, 0x16, 0x0a, 0x16, 0x08, 0x39, 0x0a,
0x39, 0x08, 0x0f, 0x0a, 0x01, 0x08, 0x0e, 0x19, 0x02, 0x18, 0x06, 0x08, 0x04, 0x0a, 0x0e, 0x19,
0x00, 0x09, 0x13, 0x0a, 0x08, 0x19, 0x03, 0x09, 0x22, 0x0a, 0x09, 0x08, 0x07, 0x0a, 0x0e, 0x19,
0x06, 0x0a, 0x39, 0x08, 0x27, 0x0a, 0x09, 0x19, 0x1b, 0x0a, 0x18, 0x08, 0x0e, 0x18, 0x0b, 0x08,
0x0b, 0x19, 0x0b, 0x08, 0x0c, 0x19, 0x09, 0x08, 0x00, 0x0a, 0x10, 0x1a, 0x06, 0x0a, 0x10, 0x1a,
0x0f, 0x0a, 0x35, 0x08, 0x06, 0x19, 0x0d, 0x08, 0x38, 0x0a, 0x05, 0x19, 0x05, 0x09, 0x05, 0x0a,
0x39, 0x08, 0x28, 0x0a, 0x05, 0x08, 0x12, 0x18, 0x0f, 0x08, 0x33, 0x0a, 0x0d, 0x19, 0x0b, 0x0a,
0x09, 0x08, 0x0f, 0x18, 0x1f, 0x08, 0x11, 0x0a, 0x08, 0x1a, 0x1e, 0x0a, 0x2b, 0x08, 0x0e, 0x19,
0x12, 0x08, 0x22, 0x0a, 0x05, 0x08, 0x11, 0x09, 0x19, 0x08, 0x1d, 0x0a, 0x03, 0x08, 0x04, 0x09,
0x01, 0xe9, 0x5c, 0x4b, 0xff, 0x19, 0x29, 0x6f, 0xdc, 0x5e, 0xb5, 0xaa, 0x17, 0x41, 0xaa, 0x35,
0x62, 0x31, 0x3b, 0x99, 0x32, 0xf6, 0xb4, 0x65, 0x90, 0xf8, 0xac, 0x06, 0x4e, 0x04, 0x30, 0xdd,
0x59, 0x00, 0x00, 0x5a, 0x00, 0x09, 0x14, 0x0a, 0x4b, 0x09, 0x11, 0x1a, 0x00, 0x19, 0x05, 0x09,
0x12, 0x19, 0x06, 0x09, 0x14, 0x08, 0x31, 0x09, 0x10, 0x1a, 0x48, 0x09, 0x1d, 0x18, 0x0b, 0x0a,
0x26, 0x09, 0x0d, 0x19, 0x31, 0x09, 0x10, 0x0a, 0x4c, 0x09, 0x19, 0x08, 0x0a, 0x19, 0x3f, 0x09,
0x1c, 0x0a, 0x4a, 0x09, 0x0c, 0x19, 0x0d, 0x0a, 0x0c, 0x09, 0x11, 0x0a, 0x0d, 0x09, 0x0e, 0x19,
0x0e, 0x09, 0x39, 0x19, 0x08, 0x09, 0x32, 0x08, 0x08, 0x09, 0x1d, 0x0a, 0x12, 0x09, 0x04, 0x19,
0x16, 0x09, 0x03, 0x19, 0x37, 0x09, 0x0f, 0x19, 0x02, 0x09, 0x15, 0x08, 0x5f, 0x09, 0x01, 0x63,
0x44, 0xd8, 0xdb, 0x02, 0x08, 0x18, 0x87, 0x66, 0xed, 0x5d, 0xa5, 0xa1, 0x08, 0x00, 0xc4, 0x01,
0x7f, 0x5d, 0x26, 0xd9, 0xe2, 0x39, 0xe1, 0x97, 0x9a, 0x16, 0x7e, 0x92, 0x96, 0x34, 0x4a, 0x00,
0x00, 0x7e, 0x00, 0x09, 0x14, 0x0a, 0x09, 0x09, 0x02, 0x19, 0x14, 0x09, 0x0f, 0x1a, 0x00, 0x0a,
0x0d, 0x08, 0x09, 0x1a, 0x12, 0x09, 0x03, 0x08, 0x08, 0x18, 0x0b, 0x09, 0x01, 0x0a, 0x01, 0x09,
0x01, 0x19, 0x09, 0x18, 0x05, 0x09, 0x0d, 0x19, 0x07, 0x09, 0x07, 0x19, 0x29, 0x0a, 0x12, 0x09,
0x0d, 0x19, 0x10, 0x09, 0x08, 0x0a, 0x01, 0x09, 0x22, 0x18, 0x19, 0x09, 0x0e, 0x1a, 0x10, 0x09,
0x0c, 0x0a, 0x0e, 0x09, 0x06, 0x18, 0x13, 0x09, 0x0b, 0x1a, 0x14, 0x09, 0x0f, 0x0a, 0x0d, 0x09,
0x05, 0x19, 0x19, 0x09, 0x0a, 0x08, 0x09, 0x09, 0x09, 0x1a, 0x18, 0x09, 0x0f, 0x19, 0x07, 0x09,
0x11, 0x19, 0x05, 0x09, 0x11, 0x19, 0x05, 0x09, 0x0e, 0x19, 0x14, 0x09, 0x0a, 0x19, 0x35, 0x09,
0x0e, 0x19, 0x03, 0x09, 0x06, 0x08, 0x07, 0x18, 0x10, 0x08, 0x09, 0x09, 0x02, 0x19, 0x25, 0x09,
0x01, 0x10, 0xbd, 0x37, 0x81, 0xab, 0xd0, 0xe6, 0xd6, 0x3f, 0xe7, 0x6b, 0x6f, 0xcf, 0x22, 0x6a,
0xa9, 0x86, 0x1b, 0xdf, 0xd2, 0x7b, 0x61, 0xaf, 0xfa, 0xbb, 0x52, 0x17, 0xfa, 0x42, 0x73, 0x30,
0xcd, 0x00, 0x00, 0x58, 0x56, 0x09, 0x0f, 0x19, 0x09, 0x09, 0x0f, 0x19, 0x09, 0x09, 0x08, 0x0a,
0x1c, 0x09, 0x05, 0x19, 0x1a, 0x09, 0x05, 0x18, 0x0d, 0x08, 0x05, 0x09, 0x0c, 0x1a, 0x13, 0x09,
0x0f, 0x19, 0x33, 0x09, 0x07, 0x19, 0x2a, 0x09, 0x05, 0x08, 0x0c, 0x09, 0x11, 0x19, 0x09, 0x09,
0x14, 0x19, 0x0e, 0x09, 0x07, 0x0a, 0x2c, 0x09, 0x10, 0x0a, 0x15, 0x09, 0x0d, 0x18, 0x0a, 0x08,
0x26, 0x09, 0x03, 0x0a, 0x06, 0x1a, 0x0c, 0x0a, 0x10, 0x09, 0x1a, 0x08, 0x3b, 0x09, 0x11, 0x1a,
0x19, 0x19, 0x0b, 0x09, 0x11, 0x18, 0x0b, 0x09, 0x05, 0x0a, 0x7a, 0x09, 0x01, 0xf8, 0xc5, 0x71,
0x92, 0x7b, 0x63, 0xaf, 0xdd, 0x7b, 0xca, 0x1a, 0xf4, 0xd3, 0xb1, 0xfe, 0x77, 0xd4, 0x95, 0x10,
0xd4, 0x00, 0xdd, 0x70, 0xcd, 0x4e, 0x8b, 0xc9, 0xa1, 0x2a, 0x32, 0x2a, 0x6c, 0x00, 0x00, 0x50,
0x00, 0x09, 0x08, 0x0a, 0x07, 0x09, 0x0c, 0x1a, 0x12, 0x08, 0x00, 0x09, 0x09, 0x19, 0x0d, 0x09,
0x11, 0x19, 0x09, 0x09, 0x12, 0x19, 0x0d, 0x08, 0x20, 0x1a, 0x65, 0x09, 0x0c, 0x08, 0x12, 0x09,
0x09, 0x08, 0x0e, 0x09, 0x0e, 0x18, 0x12, 0x09, 0x06, 0x0a, 0x6e, 0x09, 0x08, 0x19, 0x06, 0x09,
0x08, 0x08, 0x94, 0x09, 0x08, 0x0a, 0x42, 0x09, 0x0e, 0x1a, 0x00, 0x19, 0x00, 0x09, 0x07, 0x0a,
0x4e, 0x09, 0x01, 0x0a, 0x03, 0x09, 0x17, 0x19, 0x0a, 0x09, 0x10, 0x18, 0x06, 0x0a, 0x7c, 0x09,
0x01, 0x70, 0x01, 0x0d, 0x8a, 0xbc, 0xf1, 0x36, 0x6b, 0x88, 0x76, 0x3b, 0x7c, 0x94, 0x4c, 0x52,
0x1b, 0x20, 0x3f, 0x72, 0xe7, 0x24, 0xb3, 0x7a, 0x96, 0x5f, 0x09, 0xed, 0x7a, 0x20, 0x87, 0xe7,
0xa6, 0x00, 0x00, 0x42, 0x09, 0x09, 0x0a, 0x19, 0x20, 0x09, 0x15, 0x18, 0x19, 0x0a, 0x14, 0x19,
0x27, 0x0a, 0x56, 0x09, 0x23, 0x1a, 0x14, 0x19, 0x28, 0x09, 0x1a, 0x19, 0x0e, 0x09, 0x0f, 0x19,
0x2b, 0x09, 0x1e, 0x18, 0x07, 0x09, 0x05, 0x08, 0x24, 0x09, 0x1c, 0x1a, 0x14, 0x09, 0x0e, 0x19,
0x50, 0x09, 0x12, 0x19, 0x28, 0x09, 0x02, 0x19, 0x47, 0x09, 0x1e, 0x19, 0x05, 0x09, 0x05, 0x08,
0x00, 0x09, 0x03, 0x19, 0x43, 0x09, 0x01, 0x07, 0x81, 0x39, 0x07, 0x7e, 0x57, 0x75, 0x0b, 0xda,
0x51, 0x3b, 0x9c, 0x5e, 0xc6, 0x0c, 0x1f, 0x15, 0x0b, 0x68, 0x79, 0xc0, 0x92, 0x95, 0xe3, 0xcd,
0xed, 0x21, 0x37, 0x63, 0xa6, 0x94, 0x7e, 0x00, 0x00, 0x76, 0xaf, 0x09, 0x04, 0x02, 0x01, 0x01,
0x18, 0x02, 0x01, 0x01, 0x13, 0x00, 0x19, 0x04, 0x04, 0x08, 0x18, 0x09, 0x13, 0x0a, 0x0e, 0x09,
0x0f, 0x08, 0x12, 0x09, 0x0b, 0x0a, 0x71, 0x09, 0x02, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x04, 0x09,
0x00, 0x0a, 0x22, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x04, 0x09,
0x00, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x35, 0x09, 0x0a, 0x08, 0x10, 0x09, 0x08, 0x08, 0x09, 0x09,
0x08, 0x0a, 0x09, 0x09, 0x06, 0x08, 0x21, 0x09, 0x0d, 0x0a, 0x05, 0x09, 0x0f, 0x0a, 0x07, 0x09,
0x07, 0x08, 0x0d, 0x09, 0x07, 0x08, 0x35, 0x09, 0x07, 0x0a, 0x0e, 0x09, 0x10, 0x08, 0x1b, 0x09,
0x03, 0x08, 0x04, 0x09, 0x03, 0x0a, 0x40, 0x09, 0x03, 0x08, 0x04, 0x09, 0x03, 0x0a, 0xd8, 0x09,
0x01, 0x1d, 0x87, 0x1d, 0x92, 0x61, 0x27, 0xa8, 0x0f, 0xd3, 0xa1, 0xfe, 0x0c, 0x12, 0x73, 0xa5,
0xba, 0x36, 0x90, 0x9c, 0xd3, 0x2a, 0xf0, 0x6e, 0xdb, 0x84, 0xe5, 0xf3, 0x8d, 0xf9, 0x8a, 0xe8,
0xda, 0x00, 0x00, 0x46, 0x00, 0x09, 0x04, 0x08, 0x2c, 0x09, 0x0a, 0x19, 0x0c, 0x09, 0x09, 0x1a,
0x1e, 0x08, 0x33, 0x09, 0x01, 0x19, 0x06, 0x18, 0x0f, 0x08, 0x62, 0x09, 0x0a, 0x19, 0x0d, 0x1a,
0x0b, 0x18, 0x0e, 0x09, 0x06, 0x0a, 0x3d, 0x09, 0x0d, 0x1a, 0x0b, 0x09, 0x14, 0x18, 0x04, 0x0a,
0x3e, 0x09, 0x10, 0x19, 0x12, 0x09, 0x09, 0x19, 0x3d, 0x09, 0x0f, 0x19, 0x0d, 0x09, 0x11, 0x19,
0x17, 0x09, 0x04, 0x19, 0x44, 0x09, 0x1a, 0x19, 0x11, 0x09, 0x01, 0x50, 0xfd, 0xe3, 0x86, 0x49,
0xbc, 0x96, 0xd9, 0xed, 0x1a, 0x81, 0x77, 0xa2, 0xc3, 0xb0, 0xac, 0xbe, 0x0e, 0xad, 0xcb, 0x3d,
0x4e, 0x1a, 0xbb, 0x96, 0xab, 0xb3, 0x19, 0x49, 0x38, 0x7d, 0x99, 0x00, 0x00, 0x74, 0x00, 0x09,
0x07, 0x0a, 0x08, 0x09, 0x0e, 0x1a, 0x10, 0x08, 0x10, 0x09, 0x02, 0x19, 0x14, 0x09, 0x11, 0x19,
0x10, 0x09, 0x1b, 0x08, 0x04, 0x0a, 0x0f, 0x1a, 0x0b, 0x19, 0x15, 0x09, 0x08, 0x19, 0x0f, 0x09,
0x12, 0x19, 0x16, 0x09, 0x02, 0x19, 0x15, 0x09, 0x00, 0x08, 0x2b, 0x09, 0x09, 0x19, 0x0e, 0x09,
0x04, 0x19, 0x14, 0x09, 0x0c, 0x19, 0x0a, 0x09, 0x09, 0x1a, 0x12, 0x09, 0x0f, 0x18, 0x0b, 0x09,
0x04, 0x19, 0x10, 0x09, 0x17, 0x19, 0x14, 0x09, 0x0f, 0x19, 0x06, 0x09, 0x0a, 0x19, 0x46, 0x09,
0x06, 0x1a, 0x13, 0x18, 0x0f, 0x0a, 0x08, 0x1a, 0x12, 0x18, 0x41, 0x09, 0x07, 0x19, 0x0a, 0x09,
0x0c, 0x19, 0x08, 0x09, 0x03, 0x0a, 0x25, 0x19, 0x36, 0x09, 0x05, 0x19, 0x32, 0x09, 0x06, 0x19,
0x10, 0x09, 0x01, 0xc2, 0xc3, 0x5f, 0x25, 0xc5, 0x93, 0x21, 0x94, 0x06, 0x50, 0x39, 0xd8, 0xbb,
0x22, 0xa9, 0xc3, 0xc6, 0x56, 0xa9, 0xf4, 0xbe, 0x83, 0x73, 0xff, 0x4f, 0x5d, 0xfe, 0x64, 0x61,
0x3b, 0x97, 0xca, 0x00, 0x00, 0x78, 0x00, 0x09, 0x0d, 0x0a, 0x2b, 0x09, 0x03, 0x19, 0x16, 0x09,
0x11, 0x1a, 0x00, 0x19, 0x06, 0x09, 0x06, 0x08, 0x0c, 0x18, 0x25, 0x09, 0x08, 0x18, 0x1f, 0x09,
0x0b, 0x19, 0x15, 0x09, 0x03, 0x0a, 0x12, 0x09, 0x0a, 0x1a, 0x48, 0x09, 0x0a, 0x18, 0x08, 0x09,
0x05, 0x0a, 0x29, 0x09, 0x10, 0x1a, 0x07, 0x0a, 0x38, 0x09, 0x0f, 0x18, 0x00, 0x08, 0x0c, 0x0a,
0x1d, 0x09, 0x03, 0x19, 0x04, 0x18, 0x19, 0x0a, 0x19, 0x09, 0x07, 0x0a, 0x17, 0x09, 0x09, 0x19,
0x1b, 0x09, 0x04, 0x08, 0x42, 0x09, 0x00, 0x0a, 0x00, 0x08, 0x0a, 0x09, 0x04, 0x0a, 0x37, 0x09,
0x06, 0x19, 0x1a, 0x09, 0x19, 0x19, 0x1c, 0x09, 0x06, 0x1a, 0x23, 0x18, 0x1a, 0x09, 0x05, 0x19,
0x1a, 0x09, 0x07, 0x19, 0x42, 0x09, 0x07, 0x19, 0x77, 0x09, 0x07, 0x19, 0x98, 0x09, 0x01, 0xab,
0x77, 0xec, 0x80, 0x5d, 0x6a, 0x88, 0xb0, 0x6a, 0x86, 0xf8, 0x4e, 0x7d, 0xe1, 0x46, 0xf3, 0x4f,
0x88, 0x3f, 0x33, 0x59, 0x09, 0x96, 0xa4, 0xcc, 0x11, 0xad, 0x34, 0x73, 0x9c, 0x90, 0xda, 0x00,
0x00, 0x5a, 0x1a, 0x09, 0x02, 0x19, 0x17, 0x09, 0x0c, 0x18, 0x17, 0x08, 0x0d, 0x09, 0x0f, 0x1a,
0x03, 0x0a, 0x06, 0x09, 0x0e, 0x19, 0x01, 0x1a, 0x09, 0x0a, 0x07, 0x09, 0x07, 0x0a, 0x03, 0x09,
0x0c, 0x19, 0x05, 0x09, 0x02, 0x08, 0x0b, 0x09, 0x0f, 0x19, 0x09, 0x09, 0x02, 0x19, 0x0c, 0x18,
0x01, 0x19, 0x11, 0x09, 0x0d, 0x18, 0x09, 0x09, 0x0a, 0x1a, 0x0f, 0x0a, 0x08, 0x1a, 0x05, 0x0a,
0x02, 0x09, 0x05, 0x0a, 0x15, 0x09, 0x01, 0x08, 0x21, 0x18, 0x0d, 0x09, 0x24, 0x1a, 0x0a, 0x09,
0x13, 0x19, 0x04, 0x09, 0x11, 0x19, 0x05, 0x09, 0x0a, 0x19, 0x0d, 0x09, 0x01, 0xb5, 0x84, 0x9a,
0x7a, 0x5e, 0x32, 0xba, 0x2e, 0x02, 0x39, 0x7b, 0x56, 0x67, 0x3b, 0x62, 0x01, 0xca, 0x67, 0xc0,
0x84, 0xaa, 0xb5, 0x92, 0xff, 0xc1, 0xc9, 0x84, 0x11, 0xea, 0xc3, 0x5a, 0xa9, 0x00, 0x00, 0x94,
0x00, 0x09, 0x1c, 0x0a, 0x10, 0x09, 0x11, 0x19, 0x0e, 0x09, 0x11, 0x18, 0x09, 0x09, 0x12, 0x19,
0x00, 0x09, 0x04, 0x0a, 0x11, 0x1a, 0x0a, 0x09, 0x0c, 0x18, 0x13, 0x09, 0x13, 0x19, 0x05, 0x09,
0x11, 0x18, 0x06, 0x09, 0x00, 0x0a, 0x13, 0x1a, 0x0b, 0x19, 0x0a, 0x09, 0x0f, 0x18, 0x00, 0x08,
0x09, 0x09, 0x0a, 0x19, 0x0c, 0x09, 0x06, 0x19, 0x0e, 0x09, 0x00, 0x0a, 0x09, 0x1a, 0x0b, 0x09,
0x0d, 0x19, 0x0a, 0x09, 0x23, 0x19, 0x05, 0x09, 0x0a, 0x19, 0x19, 0x09, 0x07, 0x19, 0x0f, 0x09,
0x02, 0x19, 0x12, 0x09, 0x1c, 0x08, 0x01, 0x0a, 0x09, 0x1a, 0x10, 0x0a, 0x08, 0x09, 0x07, 0x19,
0x15, 0x09, 0x0d, 0x19, 0x03, 0x09, 0x06, 0x19, 0x0d, 0x09, 0x08, 0x0a, 0x11, 0x09, 0x0f, 0x18,
0x00, 0x19, 0x08, 0x09, 0x13, 0x18, 0x00, 0x09, 0x12, 0x1a, 0x05, 0x0a, 0x0c, 0x1a, 0x0a, 0x09,
0x0f, 0x19, 0x00, 0x09, 0x20, 0x08, 0x00, 0x09, 0x04, 0x08, 0x0f, 0x09, 0x09, 0x19, 0x1a, 0x09,
0x18, 0x0a, 0x0e, 0x09, 0x01, 0x23, 0x76, 0xeb, 0x7a, 0x19, 0x6e, 0x75, 0xd0, 0xca, 0xde, 0x7e,
0xfb, 0xa2, 0x0c, 0xf6, 0xb8, 0x9e, 0xea, 0x78, 0x9a, 0xb2, 0xb8, 0x55, 0x43, 0xa1, 0x9f, 0xe7,
0xc2, 0xfc, 0xad, 0x10, 0x2a, 0x00, 0x00, 0x54, 0x74, 0x09, 0x06, 0x19, 0x3e, 0x09, 0x05, 0x19,
0x2a, 0x09, 0x08, 0x19, 0x6c, 0x09, 0x06, 0x19, 0x3a, 0x09, 0x07, 0x19, 0x02, 0x09, 0x04, 0x19,
0x20, 0x09, 0x04, 0x19, 0x2c, 0x09, 0x16, 0x0a, 0x62, 0x09, 0x25, 0x08, 0x08, 0x09, 0x26, 0x0a,
0x25, 0x09, 0x1b, 0x08, 0x06, 0x18, 0x11, 0x09, 0x03, 0x0a, 0x1b, 0x09, 0x08, 0x0a, 0x3f, 0x09,
0x03, 0x19, 0x0c, 0x09, 0x03, 0x19, 0x0c, 0x09, 0x03, 0x19, 0x0d, 0x09, 0x03, 0x19, 0x28, 0x09,
0x05, 0x19, 0x3a, 0x09, 0x05, 0x19, 0x17, 0x09, 0x04, 0x19, 0x4e, 0x09, 0x01, 0xe8, 0xdb, 0xbc,
0x58, 0x6b, 0x95, 0x16, 0x81, 0x87, 0x74, 0xcf, 0x7c, 0xe2, 0x71, 0xdb, 0x8b, 0xeb, 0x74, 0xaf,
0xbb, 0x19, 0x29, 0xe4, 0xca, 0xd8, 0xc2, 0x0d, 0x25, 0xef, 0x1e, 0x9c, 0xfa, 0x00, 0x00, 0xae,
0x6b, 0x09, 0x14, 0x0a, 0x2d, 0x09, 0x02, 0x19, 0x0d, 0x18, 0x12, 0x08, 0x02, 0x09, 0x01, 0x0a,
0x00, 0x09, 0x03, 0x19, 0x0b, 0x1a, 0x04, 0x0a, 0x02, 0x09, 0x00, 0x0a, 0x0b, 0x1a, 0x02, 0x09,
0x00, 0x0a, 0x00, 0x09, 0x02, 0x19, 0x0e, 0x18, 0x12, 0x08, 0x03, 0x0a, 0x00, 0x09, 0x03, 0x19,
0x0c, 0x1a, 0x03, 0x0a, 0x01, 0x09, 0x01, 0x0a, 0x0b, 0x1a, 0x04, 0x09, 0x02, 0x19, 0x0d, 0x18,
0x13, 0x08, 0x03, 0x0a, 0x00, 0x09, 0x03, 0x19, 0x0d, 0x1a, 0x01, 0x0a, 0x01, 0x09, 0x02, 0x0a,
0x06, 0x1a, 0x09, 0x09, 0x02, 0x19, 0x0e, 0x18, 0x0f, 0x08, 0x06, 0x0a, 0x00, 0x09, 0x02, 0x19,
0x0d, 0x1a, 0x01, 0x0a, 0x03, 0x09, 0x01, 0x0a, 0x02, 0x1a, 0x0d, 0x09, 0x02, 0x19, 0x0d, 0x18,
0x11, 0x08, 0x05, 0x0a, 0x00, 0x09, 0x03, 0x19, 0x0c, 0x1a, 0x02, 0x0a, 0x01, 0x09, 0x02, 0x0a,
0x0c, 0x1a, 0x03, 0x09, 0x03, 0x19, 0x0e, 0x18, 0x11, 0x08, 0x04, 0x0a, 0x00, 0x09, 0x02, 0x19,
0x0c, 0x1a, 0x01, 0x0a, 0x04, 0x09, 0x01, 0x0a, 0x07, 0x1a, 0x33, 0x09, 0x11, 0x08, 0x11, 0x09,
0x17, 0x08, 0x25, 0x09, 0x2b, 0x0a, 0x1c, 0x09, 0x00, 0x0a, 0x14, 0x08, 0x84, 0x09, 0x01, 0xbd,
0xa2, 0x63, 0xa7, 0xa2, 0xaf, 0x15, 0x01, 0xf7, 0xbd, 0xaf, 0x4e, 0xef, 0x00, 0x11, 0x1f, 0x99,
0x77, 0x91, 0xbf, 0xd2, 0x29, 0x6e, 0x9b, 0xcb, 0x4e, 0xb0, 0x87, 0x2b, 0xf6, 0x68, 0x62, 0x00,
0x00, 0xc8, 0x45, 0x09, 0x04, 0x08, 0x04, 0x09, 0x10, 0x18, 0x09, 0x08, 0x0a, 0x09, 0x06, 0x1a,
0x0b, 0x0a, 0x03, 0x09, 0x02, 0x0a, 0x01, 0x06, 0x00, 0x05, 0x14, 0x15, 0x02, 0x05, 0x10, 0x16,
0x02, 0x05, 0x03, 0x04, 0x0c, 0x05, 0x0a, 0x15, 0x05, 0x05, 0x07, 0x06, 0x0f, 0x15, 0x02, 0x05,
0x0a, 0x04, 0x10, 0x15, 0x05, 0x05, 0x0d, 0x15, 0x10, 0x05, 0x00, 0x04, 0x04, 0x05, 0x00, 0x06,
0x04, 0x05, 0x00, 0x04, 0x04, 0x05, 0x00, 0x06, 0x02, 0x05, 0x08, 0x04, 0x10, 0x15, 0x0b, 0x14,
0x0f, 0x16, 0x0a, 0x14, 0x06, 0x05, 0x0b, 0x14, 0x10, 0x05, 0x18, 0x16, 0x29, 0x14, 0x16, 0x06,
0x03, 0x05, 0x0d, 0x06, 0x09, 0x16, 0x0b, 0x04, 0x08, 0x14, 0x02, 0x04, 0x0d, 0x05, 0x04, 0x15,
0x11, 0x05, 0x11, 0x04, 0x07, 0x05, 0x1d, 0x09, 0x02, 0x0a, 0x03, 0x19, 0x01, 0x18, 0x3f, 0x19,
0x00, 0x09, 0x0a, 0x08, 0x10, 0x09, 0x18, 0x0a, 0x3d, 0x09, 0x05, 0x19, 0x17, 0x09, 0x05, 0x19,
0x7d, 0x09, 0x07, 0x19, 0x0a, 0x09, 0x13, 0x0a, 0x56, 0x09, 0x03, 0x19, 0x05, 0x18, 0x0a, 0x08,
0x26, 0x09, 0x07, 0x08, 0x43, 0x09, 0x09, 0x19, 0x02, 0x09, 0x07, 0x0a, 0x01, 0x09, 0x00, 0x08,
0x04, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x00, 0x08,
0x04, 0x09, 0x04, 0x0a, 0x44, 0x09, 0x18, 0x19, 0x13, 0x09, 0x01, 0x80, 0x8f, 0x0e, 0x04, 0xe4,
0xb5, 0x8b, 0x4b, 0x5e, 0x7c, 0x92, 0xdc, 0x7c, 0xb2, 0x3a, 0x0b, 0x45, 0x9b, 0x4c, 0x07, 0x2b,
0xd6, 0x8d, 0x27, 0x26, 0xa1, 0xa1, 0x61, 0x72, 0x39, 0x3c, 0x9f, 0x00, 0x00, 0x86, 0x00, 0x09,
0x0c, 0x08, 0x07, 0x09, 0x0f, 0x18, 0x0b, 0x19, 0x03, 0x09, 0x02, 0x08, 0x00, 0x09, 0x0f, 0x1a,
0x12, 0x09, 0x0a, 0x1a, 0x02, 0x18, 0x04, 0x08, 0x0e, 0x18, 0x01, 0x08, 0x05, 0x09, 0x0b, 0x19,
0x07, 0x09, 0x0b, 0x19, 0x04, 0x09, 0x0a, 0x19, 0x0a, 0x09, 0x06, 0x19, 0x08, 0x09, 0x0a, 0x19,
0x0d, 0x09, 0x0c, 0x19, 0x07, 0x09, 0x0c, 0x18, 0x0a, 0x09, 0x03, 0x19, 0x11, 0x09, 0x03, 0x19,
0x08, 0x1a, 0x05, 0x09, 0x0e, 0x19, 0x06, 0x09, 0x0f, 0x19, 0x00, 0x09, 0x10, 0x1a, 0x01, 0x09,
0x0f, 0x18, 0x10, 0x09, 0x0d, 0x19, 0x05, 0x09, 0x08, 0x19, 0x09, 0x09, 0x07, 0x19, 0x0e, 0x09,
0x0a, 0x19, 0x0e, 0x09, 0x0b, 0x19, 0x08, 0x09, 0x0f, 0x19, 0x09, 0x09, 0x08, 0x19, 0x06, 0x09,
0x08, 0x0a, 0x0b, 0x09, 0x0a, 0x19, 0x0a, 0x09, 0x0e, 0x19, 0x27, 0x09, 0x00, 0x0a, 0x25, 0x09,
0x0c, 0x18, 0x93, 0x09, 0x01, 0x42, 0xea, 0x18, 0x4a, 0x33, 0xd9, 0x22, 0x20, 0x3d, 0x99, 0xfd,
0x82, 0x11, 0x9d, 0xa0, 0xb7, 0x3e, 0xe7, 0x9b, 0xa7, 0x87, 0xd8, 0xf9, 0x74, 0x29, 0x5f, 0xd8,
0x61, 0xa8, 0xa3, 0x81, 0x17, 0x00, 0x00, 0x98, 0x2b, 0x09, 0x36, 0x19, 0x12, 0x1a, 0x25, 0x19,
0x23, 0x18, 0x42, 0x19, 0x08, 0x09, 0x0d, 0x19, 0x08, 0x09, 0x11, 0x1a, 0x06, 0x09, 0x01, 0x19,
0x0d, 0x18, 0x11, 0x08, 0x02, 0x09, 0x10, 0x0a, 0x10, 0x18, 0x06, 0x0a, 0x1a, 0x09, 0x02, 0x19,
0x04, 0x1a, 0x00, 0x19, 0x06, 0x09, 0x21, 0x19, 0x07, 0x09, 0x13, 0x1a, 0x02, 0x09, 0x01, 0x08,
0x01, 0x09, 0x17, 0x19, 0x0f, 0x09, 0x36, 0x19, 0x03, 0x09, 0x0e, 0x1a, 0x06, 0x09, 0x25, 0x18,
0x13, 0x1a, 0x21, 0x19, 0x13, 0x1a, 0x2d, 0x18, 0x21, 0x1a, 0x15, 0x09, 0x14, 0x18, 0x03, 0x09,
0x10, 0x19, 0x06, 0x09, 0x06, 0x0a, 0x12, 0x19, 0x04, 0x09, 0x07, 0x18, 0x14, 0x09, 0x02, 0x0a,
0x02, 0x09, 0x0d, 0x19, 0x07, 0x09, 0x11, 0x1a, 0x04, 0x09, 0x0b, 0x19, 0x09, 0x09, 0x0f, 0x1a,
0x01, 0x19, 0x01, 0x0a, 0x00, 0x09, 0x1f, 0x19, 0x07, 0x09, 0x06, 0x08, 0x0b, 0x09, 0x09, 0x08,
0x0c, 0x09, 0x0a, 0x19, 0x1c, 0x09, 0x23, 0x05, 0x02, 0x15, 0x03, 0x16, 0x19, 0x04, 0x05, 0x05,
0x01, 0x5c, 0x04, 0x43, 0x8e, 0x64, 0xcf, 0xd5, 0x34, 0xd1, 0x17, 0x05, 0x45, 0x59, 0x90, 0xb5,
0xea, 0x93, 0x51, 0x13, 0x33, 0x18, 0xe3, 0x6a, 0x06, 0xea, 0xac, 0x7e, 0x2a, 0xe4, 0x36, 0x45,
0xdd, 0x00, 0x00, 0x46, 0x00, 0x09, 0x4d, 0x0a, 0x17, 0x09, 0x0f, 0x18, 0x0a, 0x09, 0x0f, 0x1a,
0x0c, 0x09, 0x01, 0x19, 0x0d, 0x18, 0x01, 0x0a, 0x00, 0x08, 0x01, 0x09, 0x02, 0x19, 0x0f, 0x1a,
0x0f, 0x08, 0x06, 0x09, 0x12, 0x18, 0x03, 0x19, 0x08, 0x18, 0x10, 0x19, 0x08, 0x08, 0x19, 0x09,
0x11, 0x19, 0x0a, 0x09, 0x10, 0x1a, 0x03, 0x09, 0x02, 0x0a, 0x20, 0x09, 0x07, 0x19, 0x07, 0x09,
0x11, 0x19, 0x09, 0x09, 0x11, 0x19, 0x15, 0x08, 0x7a, 0x09, 0x01, 0x79, 0x6a, 0xb2, 0x22, 0xa0,
0xd2, 0xf4, 0x74, 0x29, 0x79, 0x37, 0xc6, 0xcf, 0x70, 0x8f, 0xbe, 0xad, 0x57, 0x28, 0x41, 0x65,
0xfb, 0x12, 0x02, 0xe1, 0x7f, 0xcc, 0x45, 0xc8, 0xa4, 0x65, 0xd4, 0x00, 0x00, 0x6e, 0x00, 0x09,
0x08, 0x0a, 0x0f, 0x09, 0x0c, 0x1a, 0x07, 0x09, 0x0e, 0x1a, 0x0a, 0x09, 0x11, 0x18, 0x09, 0x08,
0x00, 0x09, 0x00, 0x08, 0x06, 0x09, 0x11, 0x18, 0x0e, 0x0a, 0x06, 0x09, 0x06, 0x19, 0x08, 0x09,
0x02, 0x19, 0x0d, 0x09, 0x12, 0x08, 0x14, 0x19, 0x06, 0x1a, 0x12, 0x0a, 0x00, 0x09, 0x0b, 0x19,
0x0b, 0x09, 0x12, 0x19, 0x0b, 0x08, 0x07, 0x09, 0x02, 0x19, 0x09, 0x09, 0x11, 0x08, 0x08, 0x1a,
0x16, 0x08, 0x1f, 0x09, 0x04, 0x1a, 0x0e, 0x0a, 0x01, 0x09, 0x0d, 0x19, 0x09, 0x09, 0x06, 0x19,
0x10, 0x09, 0x10, 0x18, 0x0f, 0x09, 0x16, 0x0a, 0x08, 0x09, 0x02, 0x19, 0x26, 0x09, 0x1f, 0x08,
0x16, 0x09, 0x12, 0x1a, 0x03, 0x0a, 0x4d, 0x09, 0x00, 0x19, 0x2e, 0x09, 0x01, 0x97, 0xad, 0x9a,
0x9b, 0x08, 0xd9, 0x9b, 0xe3, 0x1d, 0xc5, 0x2d, 0xe0, 0x4e, 0x0a, 0xb3, 0xae, 0xb3, 0xac, 0xd9,
0x76, 0x88, 0x41, 0xdd, 0xb4, 0x87, 0x26, 0x7e, 0xff, 0x0e, 0xc5, 0xf0, 0x85, 0x00, 0x00, 0x7e,
0x00, 0x09, 0x15, 0x0a, 0x12, 0x09, 0x01, 0x19, 0x15, 0x09, 0x10, 0x19, 0x0a, 0x08, 0x11, 0x09,
0x0f, 0x19, 0x08, 0x09, 0x05, 0x08, 0x06, 0x09, 0x01, 0x19, 0x27, 0x09, 0x01, 0x19, 0x1b, 0x09,
0x03, 0x19, 0x13, 0x09, 0x05, 0x19, 0x11, 0x09, 0x03, 0x0a, 0x09, 0x09, 0x04, 0x19, 0x0b, 0x09,
0x09, 0x08, 0x02, 0x09, 0x0b, 0x08, 0x09, 0x19, 0x00, 0x09, 0x01, 0x08, 0x02, 0x09, 0x02, 0x19,
0x14, 0x09, 0x13, 0x1a, 0x03, 0x09, 0x05, 0x08, 0x17, 0x09, 0x02, 0x08, 0x06, 0x0a, 0x0e, 0x1a,
0x02, 0x08, 0x0d, 0x18, 0x08, 0x08, 0x07, 0x09, 0x06, 0x08, 0x05, 0x0a, 0x0d, 0x1a, 0x16, 0x18,
0x09, 0x1a, 0x08, 0x08, 0x01, 0x09, 0x04, 0x0a, 0x00, 0x09, 0x03, 0x19, 0x08, 0x09, 0x08, 0x19,
0x09, 0x09, 0x29, 0x08, 0x05, 0x18, 0x00, 0x19, 0x12, 0x1a, 0x08, 0x0a, 0x17, 0x09, 0x01, 0x80,
0x45, 0x34, 0x57, 0x6b, 0x83, 0xfb, 0x6b, 0x07, 0x7d, 0xfb, 0x3e, 0xa4, 0x11, 0x16, 0x74, 0xee,
0x8a, 0xcf, 0x13, 0x81, 0x83, 0xab, 0x02, 0xcf, 0x22, 0x87, 0xbd, 0x7a, 0x22, 0xba, 0xcf, 0x00,
0x00, 0x48, 0x1d, 0x09, 0x03, 0x19, 0x20, 0x09, 0x0e, 0x18, 0x02, 0x08, 0x06, 0x09, 0x10, 0x19,
0x04, 0x09, 0x0f, 0x1a, 0x0b, 0x08, 0x10, 0x09, 0x03, 0x19, 0x6c, 0x09, 0x03, 0x19, 0x09, 0x09,
0x0b, 0x18, 0x03, 0x09, 0x04, 0x19, 0x12, 0x09, 0x0b, 0x19, 0x04, 0x09, 0x01, 0x08, 0x01, 0x19,
0x0d, 0x1a, 0x01, 0x09, 0x02, 0x11, 0x0f, 0x19, 0x02, 0x11, 0x11, 0x19, 0x01, 0x11, 0x09, 0x19,
0x10, 0x09, 0x04, 0x19, 0x13, 0x09, 0x00, 0x08, 0x11, 0x09, 0x01, 0xa0, 0x75, 0xe5, 0x89, 0xe7,
0x17, 0xc8, 0x47, 0x64, 0xdd, 0x35, 0xbc, 0xb0, 0x4e, 0xa4, 0x10, 0x4a, 0xde, 0xf3, 0x07, 0x64,
0x61, 0xca, 0x7a, 0x27, 0xe0, 0x41, 0xab, 0x26, 0xc8, 0x7e, 0x22, 0x00, 0x00, 0x6c, 0x12, 0x09,
0x09, 0x08, 0x04, 0x09, 0x05, 0x19, 0x0a, 0x09, 0x0d, 0x19, 0x06, 0x09, 0x01, 0x0a, 0x17, 0x09,
0x00, 0x0a, 0x22, 0x09, 0x13, 0x1a, 0x02, 0x09, 0x05, 0x19, 0x11, 0x09, 0x05, 0x19, 0x02, 0x09,
0x0f, 0x08, 0x02, 0x18, 0x12, 0x08, 0x03, 0x09, 0x03, 0x19, 0x0c, 0x09, 0x02, 0x19, 0x16, 0x09,
0x0e, 0x0a, 0x03, 0x09, 0x02, 0x19, 0x12, 0x09, 0x02, 0x19, 0x16, 0x09, 0x17, 0x19, 0x0a, 0x09,
0x07, 0x0a, 0x02, 0x09, 0x02, 0x0a, 0x04, 0x09, 0x14, 0x1a, 0x05, 0x09, 0x03, 0x08, 0x0d, 0x09,
0x11, 0x18, 0x02, 0x08, 0x01, 0x09, 0x0e, 0x19, 0x07, 0x09, 0x0e, 0x1a, 0x02, 0x09, 0x03, 0x0a,
0x22, 0x09, 0x10, 0x18, 0x0f, 0x09, 0x05, 0x08, 0x23, 0x09, 0x01, 0xa5, 0x6c, 0xc6, 0x79, 0x27,
0x58, 0x55, 0x35, 0x5b, 0xcf, 0xf1, 0x8b, 0x60, 0x01, 0xa6, 0x38, 0xf8, 0x34, 0x83, 0xf5, 0x37,
0xf0, 0xda, 0xd3, 0x92, 0xdc, 0x03, 0x40, 0xa6, 0xa8, 0x9f, 0xc8, 0x00, 0x00, 0x56, 0x1f, 0x09,
0x00, 0x0a, 0x09, 0x1a, 0x08, 0x0a, 0x01, 0x09, 0x11, 0x19, 0x00, 0x09, 0x0d, 0x08, 0x04, 0x09,
0x08, 0x18, 0x06, 0x09, 0x0a, 0x0a, 0x07, 0x09, 0x10, 0x19, 0x01, 0x09, 0x11, 0x19, 0x15, 0x1a,
0x04, 0x19, 0x09, 0x09, 0x07, 0x18, 0x21, 0x09, 0x08, 0x1a, 0x00, 0x09, 0x03, 0x08, 0x06, 0x19,
0x01, 0x09, 0x11, 0x08, 0x27, 0x09, 0x10, 0x1a, 0x0c, 0x09, 0x03, 0x19, 0x1e, 0x09, 0x0f, 0x18,
0x07, 0x08, 0x08, 0x18, 0x09, 0x08, 0x03, 0x09, 0x10, 0x1a, 0x08, 0x09, 0x0f, 0x19, 0x07, 0x09,
0x11, 0x08, 0x1c, 0x09, 0x01, 0x33, 0xbf, 0x71, 0xd7, 0xf3, 0xb4, 0x74, 0xf8, 0xde, 0xca, 0x73,
0x65, 0xf7, 0x81, 0xe0, 0x3e, 0x57, 0x10, 0x91, 0x99, 0xb8, 0x50, 0x4f, 0x02, 0xc0, 0xcf, 0x17,
0x8a, 0xad, 0x67, 0xda, 0xce, 0x00, 0x00, 0x6a, 0x17, 0x09, 0x01, 0x19, 0x15, 0x09, 0x0f, 0x19,
0x0d, 0x09, 0x07, 0x19, 0x77, 0x09, 0x0d, 0x1a, 0x00, 0x09, 0x05, 0x0a, 0x11, 0x19, 0x05, 0x09,
0x09, 0x19, 0x0e, 0x09, 0x03, 0x19, 0x11, 0x09, 0x0a, 0x08, 0x08, 0x18, 0x10, 0x08, 0x07, 0x09,
0x02, 0x08, 0x03, 0x19, 0x06, 0x09, 0x00, 0x08, 0x34, 0x09, 0x03, 0x19, 0x0e, 0x09, 0x06, 0x19,
0x09, 0x09, 0x05, 0x0a, 0x03, 0x09, 0x03, 0x08, 0x01, 0x09, 0x02, 0x0a, 0x02, 0x09, 0x02, 0x08,
0x01, 0x09, 0x04, 0x0a, 0x17, 0x09, 0x05, 0x19, 0x0b, 0x09, 0x07, 0x0a, 0x0f, 0x1a, 0x0b, 0x0a,
0x41, 0x09, 0x05, 0x19, 0x10, 0x09, 0x07, 0x19, 0x0a, 0x09, 0x10, 0x19, 0x08, 0x09, 0x04, 0x19,
0x24, 0x09, 0x01, 0xde, 0xb2, 0xcf, 0x2f, 0x8e, 0xcf, 0x5c, 0x1d, 0xce, 0x78, 0x6a, 0x93, 0xbc,
0x32, 0x14, 0x11, 0x7f, 0xb1, 0xa2, 0xce, 0xf2, 0x0b, 0x50, 0xe1, 0x59, 0xed, 0x58, 0x37, 0x49,
0x2d, 0x8a, 0xe2, 0x00, 0x00, 0x44, 0x26, 0x09, 0x01, 0x19, 0x14, 0x09, 0x06, 0x1a, 0x11, 0x0a,
0x16, 0x09, 0x0c, 0x19, 0x06, 0x09, 0x0b, 0x18, 0x01, 0x08, 0x09, 0x0a, 0x02, 0x1a, 0x12, 0x0a,
0x37, 0x09, 0x02, 0x19, 0x0d, 0x09, 0x02, 0x11, 0x0f, 0x19, 0x11, 0x18, 0x29, 0x09, 0x05, 0x18,
0x1e, 0x08, 0x05, 0x09, 0x10, 0x1a, 0x10, 0x0a, 0x28, 0x09, 0x10, 0x18, 0x0f, 0x08, 0x13, 0x09,
0x11, 0x1a, 0x0a, 0x0a, 0x03, 0x09, 0x03, 0x08, 0x1f, 0x09, 0x01, 0x95, 0x30, 0x64, 0x64, 0x72,
0x84, 0x53, 0x0b, 0x15, 0x8f, 0x75, 0x00, 0x7b, 0xf1, 0xd2, 0xf2, 0x4d, 0xa6, 0x72, 0x37, 0x34,
0x2f, 0xa3, 0x7e, 0xcb, 0x72, 0x4f, 0xff, 0x60, 0x56, 0xe9, 0xb3, 0x00, 0x00, 0x64, 0x05, 0x09,
0x0c, 0x1a, 0x01, 0x0a, 0x01, 0x09, 0x1e, 0x1a, 0x0f, 0x0a, 0x3c, 0x09, 0x03, 0x19, 0x0f, 0x09,
0x01, 0x19, 0x18, 0x09, 0x02, 0x19, 0x1d, 0x09, 0x07, 0x08, 0x02, 0x09, 0x24, 0x08, 0x0e, 0x1a,
0x0b, 0x0a, 0x06, 0x09, 0x0b, 0x18, 0x10, 0x1a, 0x1c, 0x08, 0x02, 0x19, 0x08, 0x18, 0x07, 0x0a,
0x0f, 0x09, 0x0e, 0x1a, 0x09, 0x0a, 0x14, 0x09, 0x07, 0x1a, 0x1f, 0x08, 0x03, 0x09, 0x08, 0x19,
0x13, 0x09, 0x0d, 0x1a, 0x05, 0x08, 0x17, 0x09, 0x01, 0x19, 0x14, 0x09, 0x02, 0x19, 0x0e, 0x09,
0x07, 0x1a, 0x16, 0x08, 0x00, 0x09, 0x0f, 0x19, 0x05, 0x18, 0x0c, 0x0a, 0x10, 0x09, 0x00, 0x19,
0x15, 0x09, 0x01, 0x62, 0xa4, 0x2b, 0x64, 0x8f, 0xc4, 0x8c, 0xd2, 0x7c, 0xa6, 0x71, 0xd0, 0x15,
0xc0, 0x14, 0xe2, 0x5c, 0x07, 0x03, 0xf3, 0x8f, 0xa6, 0x97, 0xce, 0x9e, 0x4d, 0xc1, 0x44, 0x7c,
0xec, 0x0c, 0xb9, 0x00, 0x00, 0x4c, 0x00, 0x09, 0x05, 0x0a, 0x04, 0x1a, 0x1a, 0x0a, 0x07, 0x09,
0x01, 0x1a, 0x00, 0x16, 0x10, 0x1a, 0x0c, 0x08, 0x08, 0x09, 0x01, 0x19, 0x00, 0x11, 0x10, 0x19,
0x0c, 0x0a, 0x14, 0x09, 0x0a, 0x18, 0x08, 0x09, 0x01, 0x08, 0x02, 0x09, 0x0a, 0x19, 0x1e, 0x09,
0x00, 0x08, 0x06, 0x09, 0x09, 0x19, 0x23, 0x09, 0x0b, 0x1a, 0x03, 0x09, 0x07, 0x18, 0x19, 0x08,
0x0b, 0x09, 0x01, 0x19, 0x13, 0x09, 0x09, 0x1a, 0x09, 0x0a, 0x00, 0x08, 0x10, 0x09, 0x01, 0x19,
0x18, 0x09, 0x01, 0xfc, 0x2e, 0x46, 0xcf, 0x8d, 0xe9, 0xdd, 0xd4, 0x1a, 0xd6, 0xfc, 0xf4, 0x60,
0x69, 0x83, 0x05, 0xbf, 0x89, 0x27, 0x40, 0x4c, 0xc8, 0x74, 0xc8, 0xed, 0x4e, 0x48, 0x5f, 0x5c,
0xdb, 0xf7, 0xc5, 0x00, 0x00, 0x4a, 0x2a, 0x09, 0x02, 0x08, 0x1a, 0x09, 0x04, 0x1a, 0x00, 0x16,
0x0b, 0x1a, 0x02, 0x0a, 0x11, 0x08, 0x14, 0x09, 0x0e, 0x1a, 0x05, 0x19, 0x19, 0x09, 0x11, 0x18,
0x01, 0x09, 0x02, 0x0a, 0x0e, 0x19, 0x16, 0x09, 0x0b, 0x1a, 0x00, 0x19, 0x06, 0x09, 0x01, 0x08,
0x03, 0x19, 0x15, 0x09, 0x11, 0x18, 0x13, 0x1a, 0x0e, 0x09, 0x10, 0x18, 0x04, 0x08, 0x01, 0x09,
0x10, 0x1a, 0x04, 0x09, 0x07, 0x1a, 0x0b, 0x09, 0x01, 0x08, 0x11, 0x09, 0x02, 0x19, 0x1b, 0x09,
0x01, 0x9e, 0x9c, 0x0f, 0x47, 0x3c, 0x0c, 0x0f, 0x15, 0xb8, 0xd1, 0x27, 0xe6, 0x54, 0xe0, 0x33,
0x96, 0x7b, 0x73, 0x0e, 0x0b, 0x95, 0xe1, 0xf7, 0x83, 0x6b, 0xc3, 0x1c, 0x6c, 0xb4, 0x3e, 0xfa,
0x26, 0x00, 0x00, 0x46, 0x07, 0x09, 0x03, 0x1a, 0x14, 0x09, 0x11, 0x1a, 0x02, 0x09, 0x11, 0x18,
0x06, 0x09, 0x12, 0x18, 0x08, 0x09, 0x12, 0x1a, 0x02, 0x09, 0x01, 0x08, 0x10, 0x18, 0x01, 0x08,
0x02, 0x09, 0x0b, 0x1a, 0x0b, 0x09, 0x09, 0x1a, 0x07, 0x09, 0x02, 0x08, 0x02, 0x09, 0x12, 0x18,
0x00, 0x19, 0x02, 0x09, 0x0d, 0x18, 0x12, 0x1a, 0x0c, 0x0a, 0x31, 0x09, 0x09, 0x1a, 0x0d, 0x09,
0x11, 0x19, 0x0b, 0x09, 0x12, 0x18, 0x03, 0x08, 0x23, 0x09, 0x01, 0xd6, 0x8f, 0xfd, 0x56, 0xf9,
0x05, 0x67, 0x54, 0x1a, 0xb7, 0xf2, 0x8b, 0x14, 0x1c, 0x86, 0x99, 0x99, 0xb6, 0xb0, 0xf6, 0x58,
0xa8, 0x7a, 0x48, 0x26, 0x4d, 0x30, 0x43, 0xe8, 0x5d, 0x62, 0xfd, 0x00, 0x00, 0x48, 0x2b, 0x09,
0x05, 0x0a, 0x01, 0x1a, 0x14, 0x0a, 0x04, 0x09, 0x0d, 0x18, 0x09, 0x09, 0x07, 0x08, 0x07, 0x18,
0x07, 0x08, 0x08, 0x09, 0x03, 0x19, 0x0d, 0x09, 0x02, 0x0a, 0x0b, 0x1a, 0x23, 0x09, 0x0c, 0x19,
0x05, 0x18, 0x04, 0x19, 0x05, 0x18, 0x06, 0x08, 0x34, 0x09, 0x04, 0x1a, 0x02, 0x0a, 0x0e, 0x1a,
0x20, 0x09, 0x07, 0x0a, 0x09, 0x09, 0x07, 0x0a, 0x0a, 0x09, 0x08, 0x0a, 0x05, 0x09, 0x02, 0x19,
0x06, 0x09, 0x0b, 0x08, 0x12, 0x09, 0x01, 0x0c, 0x85, 0x6c, 0x94, 0x12, 0x2d, 0xb3, 0x09, 0xb8,
0xfc, 0x00, 0xd5, 0x0a, 0xe7, 0x68, 0x2e, 0xa5, 0x73, 0x3f, 0x2c, 0x46, 0xc8, 0x87, 0x2b, 0x05,
0x8b, 0x80, 0x06, 0xb6, 0x3f, 0xcc, 0x43, 0x00, 0x00, 0x72, 0x00, 0x09, 0x04, 0x08, 0x05, 0x18,
0x17, 0x08, 0x0c, 0x09, 0x01, 0x18, 0x00, 0x14, 0x0c, 0x18, 0x17, 0x0a, 0x00, 0x09, 0x04, 0x0a,
0x0d, 0x09, 0x01, 0x19, 0x0e, 0x18, 0x14, 0x09, 0x01, 0x19, 0x15, 0x09, 0x02, 0x19, 0x04, 0x1a,
0x15, 0x0a, 0x33, 0x09, 0x02, 0x19, 0x0b, 0x09, 0x04, 0x19, 0x12, 0x09, 0x07, 0x19, 0x0c, 0x09,
0x03, 0x19, 0x16, 0x09, 0x01, 0x19, 0x21, 0x09, 0x03, 0x19, 0x07, 0x09, 0x0b, 0x0a, 0x01, 0x19,
0x09, 0x18, 0x05, 0x08, 0x1d, 0x19, 0x15, 0x09, 0x03, 0x1a, 0x10, 0x09, 0x09, 0x08, 0x06, 0x09,
0x01, 0x19, 0x29, 0x09, 0x07, 0x08, 0x01, 0x09, 0x02, 0x19, 0x12, 0x09, 0x02, 0x19, 0x30, 0x09,
0x07, 0x08, 0x02, 0x18, 0x00, 0x08, 0x1a, 0x09, 0x00, 0x19, 0x46, 0x09, 0x01, 0x5a, 0xe6, 0x2e,
0xe2, 0x90, 0x50, 0x3d, 0x01, 0xe2, 0x17, 0x98, 0x86, 0x60, 0x55, 0x85, 0xec, 0x25, 0x8c, 0x32,
0xd2, 0x1c, 0xe6, 0x0e, 0x29, 0xff, 0x99, 0x1b, 0x06, 0x4f, 0x13, 0xb1, 0x5a, 0x00, 0x00, 0x84,
0x0a, 0x09, 0x0b, 0x08, 0x02, 0x09, 0x07, 0x19, 0x01, 0x09, 0x02, 0x08, 0x03, 0x19, 0x08, 0x1a,
0x12, 0x0a, 0x10, 0x09, 0x02, 0x19, 0x08, 0x09, 0x06, 0x0a, 0x0c, 0x09, 0x07, 0x08, 0x05, 0x18,
0x0c, 0x08, 0x13, 0x09, 0x07, 0x18, 0x07, 0x08, 0x0a, 0x09, 0x12, 0x1a, 0x04, 0x0a, 0x17, 0x09,
0x03, 0x19, 0x0e, 0x09, 0x0d, 0x08, 0x07, 0x09, 0x0c, 0x0a, 0x05, 0x09, 0x05, 0x1a, 0x0e, 0x0a,
0x02, 0x09, 0x02, 0x19, 0x15, 0x09, 0x0c, 0x19, 0x11, 0x09, 0x01, 0x08, 0x01, 0x09, 0x05, 0x08,
0x02, 0x19, 0x08, 0x18, 0x0a, 0x09, 0x07, 0x19, 0x07, 0x09, 0x0c, 0x08, 0x11, 0x09, 0x04, 0x19,
0x12, 0x09, 0x05, 0x19, 0x09, 0x09, 0x16, 0x1a, 0x0b, 0x19, 0x04, 0x09, 0x01, 0x08, 0x12, 0x09,
0x0c, 0x18, 0x08, 0x09, 0x01, 0x0a, 0x08, 0x09, 0x03, 0x19, 0x13, 0x09, 0x12, 0x19, 0x07, 0x09,
0x01, 0x19, 0x17, 0x09, 0x01, 0x95, 0x44, 0xe4, 0x43, 0x33, 0x1c, 0xac, 0xae, 0x01, 0xc8, 0x3d,
0x52, 0x19, 0x0b, 0x40, 0x54, 0x6f, 0xd4, 0x50, 0x63, 0xfe, 0xbc, 0x00, 0x76, 0x38, 0x15, 0xd6,
0xc2, 0xdf, 0x79, 0xef, 0x13, 0x00, 0x00, 0x7c, 0x00, 0x09, 0x13, 0x08, 0x02, 0x09, 0x01, 0x19,
0x16, 0x09, 0x02, 0x18, 0x00, 0x14, 0x05, 0x18, 0x07, 0x08, 0x01, 0x1a, 0x00, 0x16, 0x0a, 0x1a,
0x00, 0x0a, 0x04, 0x09, 0x07, 0x08, 0x00, 0x19, 0x00, 0x15, 0x05, 0x19, 0x0b, 0x09, 0x01, 0x1a,
0x00, 0x16, 0x09, 0x1a, 0x09, 0x0a, 0x03, 0x09, 0x01, 0x19, 0x17, 0x09, 0x0f, 0x18, 0x03, 0x08,
0x0c, 0x19, 0x0a, 0x09, 0x03, 0x19, 0x0f, 0x09, 0x07, 0x1a, 0x0c, 0x09, 0x00, 0x08, 0x24, 0x18,
0x06, 0x08, 0x1b, 0x09, 0x01, 0x08, 0x04, 0x1a, 0x19, 0x09, 0x02, 0x19, 0x18, 0x09, 0x00, 0x19,
0x03, 0x11, 0x01, 0x19, 0x0a, 0x09, 0x0d, 0x18, 0x08, 0x09, 0x05, 0x0a, 0x11, 0x1a, 0x04, 0x0a,
0x18, 0x09, 0x01, 0x08, 0x02, 0x18, 0x14, 0x08, 0x16, 0x09, 0x01, 0x19, 0x10, 0x09, 0x07, 0x1a,
0x18, 0x0a, 0x12, 0x09, 0x01, 0x10, 0x0d, 0x38, 0xaf, 0x4a, 0x93, 0x6e, 0xd2, 0x65, 0x2f, 0x1f,
0xa4, 0xed, 0x4b, 0xe4, 0x9a, 0x1a, 0x7a, 0x3a, 0x4b, 0x3d, 0x73, 0x11, 0xdf, 0xbe, 0x9d, 0x37,
0x0f, 0xd9, 0xb0, 0xa0, 0xb4, 0x00, 0x00, 0x58, 0x00, 0x09, 0x10, 0x0a, 0x0e, 0x09, 0x01, 0x19,
0x15, 0x09, 0x02, 0x19, 0x06, 0x18, 0x0a, 0x0a, 0x0a, 0x19, 0x05, 0x09, 0x11, 0x1a, 0x04, 0x0a,
0x01, 0x09, 0x0a, 0x18, 0x0d, 0x09, 0x02, 0x19, 0x0f, 0x1a, 0x0e, 0x18, 0x08, 0x1a, 0x02, 0x0a,
0x09, 0x1a, 0x11, 0x0a, 0x04, 0x09, 0x06, 0x19, 0x0c, 0x09, 0x05, 0x0a, 0x13, 0x09, 0x02, 0x08,
0x0d, 0x18, 0x06, 0x08, 0x02, 0x09, 0x0d, 0x19, 0x08, 0x09, 0x0f, 0x1a, 0x06, 0x09, 0x10, 0x18,
0x09, 0x09, 0x02, 0x19, 0x0d, 0x18, 0x15, 0x1a, 0x01, 0x0a, 0x15, 0x09, 0x04, 0x19, 0x12, 0x09,
0x01, 0x50, 0xcc, 0xb3, 0x5b, 0xe2, 0x8a, 0xfd, 0x51, 0xfe, 0x6d, 0xa4, 0x75, 0x1f, 0x55, 0x1f,
0x4c, 0x14, 0x6b, 0x89, 0xa9, 0x29, 0x29, 0xf6, 0x51, 0x16, 0xc6, 0x02, 0xbd, 0x21, 0x91, 0x00,
0xf9, 0x00, 0x00, 0x84, 0x00, 0x09, 0x44, 0x08, 0x0c, 0x09, 0x20, 0x0a, 0x27, 0x09, 0x07, 0x08,
0x09, 0x09, 0x06, 0x0a, 0x0a, 0x09, 0x07, 0x0a, 0x0f, 0x09, 0x06, 0x08, 0x04, 0x09, 0x07, 0x08,
0x0f, 0x09, 0x08, 0x0a, 0x20, 0x09, 0x0e, 0x0a, 0x2c, 0x09, 0x07, 0x08, 0x0e, 0x09, 0x07, 0x08,
0x16, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x06, 0x0a, 0x34, 0x09, 0x07, 0x08, 0x10, 0x09, 0x07, 0x08,
0x1c, 0x09, 0x07, 0x08, 0x08, 0x09, 0x07, 0x0a, 0x08, 0x09, 0x07, 0x08, 0x0e, 0x09, 0x08, 0x08,
0x2d, 0x09, 0x17, 0x0a, 0x05, 0x09, 0x02, 0x0a, 0x0e, 0x09, 0x18, 0x08, 0x02, 0x09, 0x00, 0x0a,
0x05, 0x09, 0x10, 0x0a, 0x19, 0x09, 0x07, 0x08, 0x18, 0x09, 0x07, 0x0a, 0x07, 0x09, 0x07, 0x08,
0x85, 0x09, 0x07, 0x1a, 0x02, 0x19, 0x42, 0x09, 0x06, 0x08, 0x4c, 0x09, 0x05, 0x0a, 0x1d, 0x09,
0x05, 0x08, 0x36, 0x09, 0x0f, 0x0a, 0x1e, 0x09, 0x01, 0xf3, 0xf6, 0xc9, 0xf2, 0xb6, 0x95, 0xea,
0x6c, 0xc7, 0xa1, 0x70, 0xeb, 0x43, 0xb3, 0x3a, 0xe7, 0xd3, 0x64, 0x15, 0xd0, 0x96, 0x0e, 0x96,
0x47, 0xa4, 0xee, 0xfd, 0x04, 0x01, 0x4c, 0xe5, 0xba, 0x00, 0x00, 0x64, 0x19, 0x09, 0x0f, 0x19,
0x01, 0x0a, 0x14, 0x1a, 0x00, 0x0a, 0x02, 0x09, 0x11, 0x18, 0x02, 0x09, 0x01, 0x15, 0x10, 0x19,
0x02, 0x09, 0x02, 0x19, 0x12, 0x09, 0x14, 0x18, 0x10, 0x1a, 0x00, 0x19, 0x03, 0x09, 0x04, 0x19,
0x0d, 0x09, 0x0b, 0x08, 0x01, 0x09, 0x09, 0x08, 0x08, 0x19, 0x09, 0x09, 0x23, 0x1a, 0x00, 0x0a,
0x01, 0x09, 0x01, 0x08, 0x06, 0x09, 0x01, 0x19, 0x0b, 0x09, 0x12, 0x08, 0x2d, 0x09, 0x02, 0x0a,
0x05, 0x1a, 0x08, 0x0a, 0x04, 0x09, 0x11, 0x19, 0x12, 0x18, 0x08, 0x08, 0x02, 0x09, 0x02, 0x19,
0x08, 0x1a, 0x06, 0x09, 0x0d, 0x08, 0x07, 0x09, 0x13, 0x1a, 0x00, 0x19, 0x0c, 0x08, 0x12, 0x09,
0x01, 0xc6, 0x5c, 0x2b, 0xd7, 0xcc, 0x8e, 0x41, 0x44, 0x5d, 0xdf, 0xc1, 0x8f, 0x12, 0xca, 0x78,
0x0b, 0x05, 0x8d, 0x2a, 0x72, 0x1e, 0xea, 0xd6, 0xba, 0xc4, 0x48, 0x7a, 0xae, 0xd3, 0x4b, 0x19,
0xe7, 0x00, 0x00, 0x4a, 0x02, 0x09, 0x05, 0x0a, 0x16, 0x1a, 0x0b, 0x19, 0x01, 0x09, 0x05, 0x08,
0x0b, 0x09, 0x03, 0x19, 0x17, 0x09, 0x0e, 0x19, 0x0b, 0x09, 0x06, 0x19, 0x11, 0x09, 0x02, 0x19,
0x0f, 0x18, 0x01, 0x09, 0x01, 0x0a, 0x00, 0x09, 0x03, 0x19, 0x14, 0x09, 0x02, 0x19, 0x14, 0x09,
0x02, 0x19, 0x0a, 0x18, 0x08, 0x09, 0x0e, 0x0a, 0x0f, 0x19, 0x0e, 0x09, 0x01, 0x0a, 0x24, 0x09,
0x0f, 0x1a, 0x06, 0x08, 0x0c, 0x09, 0x01, 0x19, 0x16, 0x09, 0x10, 0x08, 0x0a, 0x09, 0x01, 0x72,
0xc6, 0x0b, 0x9e, 0xfd, 0xe8, 0xe8, 0x2e, 0x92, 0x84, 0xdd, 0xfb, 0x8d, 0xa8, 0x64, 0x84, 0xd2,
0x2f, 0x62, 0x31, 0x53, 0xbf, 0x89, 0xce, 0x8d, 0xc5, 0x76, 0x43, 0xc6, 0x7b, 0x5a, 0x66, 0x00,
0x00, 0x46, 0x00, 0x09, 0x06, 0x08, 0x22, 0x1a, 0x03, 0x18, 0x08, 0x08, 0x25, 0x19, 0x05, 0x09,
0x03, 0x08, 0x05, 0x09, 0x04, 0x19, 0x23, 0x09, 0x18, 0x19, 0x1d, 0x09, 0x18, 0x18, 0x12, 0x09,
0x00, 0x08, 0x12, 0x19, 0x25, 0x09, 0x04, 0x08, 0x02, 0x15, 0x0a, 0x16, 0x06, 0x0a, 0x13, 0x09,
0x02, 0x0a, 0x04, 0x09, 0x0d, 0x18, 0x36, 0x09, 0x21, 0x1a, 0x0d, 0x08, 0x15, 0x19, 0x12, 0x09,
0x16, 0x0a, 0x05, 0x09, 0x11, 0x19, 0x31, 0x09, 0x01, 0xe4, 0xb5, 0x9f, 0x25, 0xb6, 0x7a, 0x4e,
0x37, 0x0f, 0x5d, 0x0a, 0x87, 0xee, 0xe9, 0x9c, 0x05, 0xef, 0x74, 0xea, 0xf8, 0xd8, 0x27, 0x03,
0x87, 0xa7, 0xbd, 0xda, 0x02, 0x16, 0x3f, 0xbc, 0xc0, 0x00, 0x00, 0x66, 0x00, 0x09, 0x04, 0x08,
0x0e, 0x18, 0x0d, 0x08, 0x0b, 0x18, 0x03, 0x08, 0x10, 0x09, 0x03, 0x0a, 0x12, 0x1a, 0x05, 0x0a,
0x02, 0x1a, 0x0e, 0x0a, 0x10, 0x09, 0x01, 0x19, 0x0e, 0x09, 0x0e, 0x18, 0x01, 0x09, 0x02, 0x0a,
0x00, 0x09, 0x05, 0x08, 0x04, 0x09, 0x0d, 0x18, 0x01, 0x09, 0x03, 0x19, 0x0b, 0x1a, 0x07, 0x0a,
0x10, 0x19, 0x03, 0x09, 0x01, 0x19, 0x11, 0x09, 0x01, 0x19, 0x26, 0x09, 0x0f, 0x1a, 0x05, 0x09,
0x08, 0x19, 0x15, 0x09, 0x0a, 0x18, 0x05, 0x08, 0x29, 0x09, 0x01, 0x19, 0x1b, 0x09, 0x04, 0x19,
0x1a, 0x09, 0x0d, 0x1a, 0x0e, 0x09, 0x02, 0x18, 0x0d, 0x08, 0x0e, 0x09, 0x07, 0x1a, 0x11, 0x0a,
0x1d, 0x09, 0x01, 0x4e, 0x11, 0xca, 0xcc, 0x1c, 0xd0, 0xbc, 0xfd, 0x36, 0x97, 0xd2, 0x85, 0x2e,
0xad, 0x81, 0x07, 0x6b, 0x72, 0xca, 0xe1, 0xb1, 0x1e, 0x9f, 0x7e, 0x57, 0xb3, 0x43, 0x76, 0xf2,
0xaf, 0x27, 0x78, 0x00, 0x00, 0x60, 0x00, 0x09, 0x13, 0x08, 0x20, 0x09, 0x02, 0x19, 0x16, 0x09,
0x04, 0x14, 0x08, 0x18, 0x0c, 0x08, 0x0c, 0x09, 0x0b, 0x1a, 0x07, 0x09, 0x00, 0x08, 0x0e, 0x09,
0x13, 0x1a, 0x14, 0x18, 0x04, 0x09, 0x0b, 0x19, 0x13, 0x09, 0x0c, 0x1a, 0x04, 0x09, 0x00, 0x08,
0x52, 0x09, 0x05, 0x19, 0x0e, 0x09, 0x08, 0x19, 0x0c, 0x09, 0x11, 0x18, 0x07, 0x08, 0x0f, 0x1a,
0x00, 0x09, 0x02, 0x08, 0x07, 0x09, 0x11, 0x1a, 0x13, 0x18, 0x00, 0x08, 0x01, 0x09, 0x00, 0x0a,
0x07, 0x09, 0x0b, 0x19, 0x2a, 0x09, 0x05, 0x19, 0x12, 0x09, 0x12, 0x19, 0x05, 0x09, 0x10, 0x1a,
0x0a, 0x09, 0x03, 0x08, 0x30, 0x09, 0x01, 0x99, 0x51, 0x74, 0xbe, 0xa6, 0xd1, 0xee, 0x36, 0x5d,
0xec, 0xb1, 0x1f, 0xbd, 0x8b, 0xd2, 0x40, 0xf5, 0xf9, 0xa1, 0x89, 0x0b, 0xd9, 0xff, 0x82, 0x26,
0x79, 0x56, 0xcc, 0x58, 0x91, 0x10, 0xb2, 0x00, 0x00, 0x6a, 0x38, 0x09, 0x24, 0x0a, 0x00, 0x09,
0x06, 0x18, 0x13, 0x08, 0x0f, 0x09, 0x00, 0x0a, 0x0a, 0x09, 0x0d, 0x0a, 0x02, 0x09, 0x00, 0x08,
0x04, 0x09, 0x01, 0x19, 0x05, 0x18, 0x00, 0x19, 0x0a, 0x09, 0x01, 0x19, 0x06, 0x09, 0x01, 0x0a,
0x00, 0x09, 0x02, 0x19, 0x08, 0x09, 0x05, 0x0a, 0x04, 0x19, 0x03, 0x09, 0x05, 0x0a, 0x02, 0x09,
0x02, 0x19, 0x1a, 0x09, 0x07, 0x08, 0x03, 0x09, 0x07, 0x08, 0x03, 0x09, 0x06, 0x08, 0x04, 0x09,
0x06, 0x0a, 0x0a, 0x09, 0x07, 0x08, 0x21, 0x09, 0x04, 0x19, 0x03, 0x09, 0x06, 0x08, 0x19, 0x09,
0x05, 0x0a, 0x0b, 0x09, 0x05, 0x08, 0x05, 0x09, 0x05, 0x0a, 0x0b, 0x09, 0x10, 0x0a, 0x30, 0x09,
0x13, 0x08, 0x0b, 0x09, 0x01, 0x23, 0xab, 0x56, 0x5c, 0x52, 0x1d, 0xfd, 0x5f, 0x05, 0xc5, 0x6c,
0x0c, 0xd3, 0x64, 0x19, 0x10, 0x92, 0x14, 0x8a, 0x01, 0x19, 0x64, 0x15, 0x32, 0xef, 0x5e, 0x36,
0x64, 0x16, 0x40, 0x7f, 0x15, 0x00, 0x00, 0x66, 0x07, 0x09, 0x11, 0x1a, 0x06, 0x09, 0x12, 0x1a,
0x03, 0x09, 0x02, 0x08, 0x03, 0x09, 0x0b, 0x18, 0x0e, 0x09, 0x01, 0x19, 0x0b, 0x18, 0x09, 0x0a,
0x01, 0x09, 0x02, 0x19, 0x11, 0x09, 0x07, 0x19, 0x0b, 0x09, 0x04, 0x0a, 0x08, 0x1a, 0x0d, 0x09,
0x12, 0x18, 0x13, 0x08, 0x14, 0x09, 0x0c, 0x1a, 0x06, 0x09, 0x03, 0x08, 0x17, 0x09, 0x03, 0x19,
0x13, 0x09, 0x06, 0x18, 0x00, 0x19, 0x11, 0x09, 0x11, 0x1a, 0x02, 0x09, 0x01, 0x08, 0x0c, 0x09,
0x01, 0x0a, 0x05, 0x1a, 0x00, 0x0a, 0x13, 0x09, 0x03, 0x08, 0x18, 0x09, 0x11, 0x18, 0x05, 0x09,
0x02, 0x19, 0x0f, 0x09, 0x02, 0x0a, 0x0c, 0x1a, 0x1a, 0x18, 0x06, 0x08, 0x1f, 0x09, 0x01, 0xca,
0xe5, 0xba, 0x14, 0xa4, 0x86, 0x58, 0x55, 0xd7, 0x91, 0xce, 0x47, 0x98, 0x52, 0x9c, 0xa2, 0x20,
0x19, 0xc2, 0x49, 0xe4, 0xe9, 0x7a, 0x96, 0xa6, 0xe4, 0x00, 0x10, 0x6a, 0x8e, 0x09, 0x61, 0x00,
0x00, 0x56, 0x00, 0x09, 0x0d, 0x08, 0x02, 0x09, 0x0a, 0x18, 0x0c, 0x09, 0x05, 0x19, 0x0b, 0x09,
0x14, 0x19, 0x0a, 0x0a, 0x02, 0x09, 0x02, 0x19, 0x10, 0x09, 0x0c, 0x19, 0x06, 0x09, 0x03, 0x08,
0x20, 0x09, 0x01, 0x19, 0x07, 0x1a, 0x0b, 0x09, 0x00, 0x08, 0x10, 0x09, 0x0b, 0x19, 0x0d, 0x09,
0x13, 0x19, 0x3f, 0x09, 0x01, 0x19, 0x0c, 0x1a, 0x23, 0x09, 0x02, 0x19, 0x08, 0x09, 0x11, 0x19,
0x0f, 0x08, 0x0f, 0x18, 0x0d, 0x08, 0x2c, 0x09, 0x03, 0x19, 0x14, 0x09, 0x0f, 0x0a, 0x01, 0x09,
0x05, 0x19, 0x1e, 0x09, 0x01, 0x19, 0x2c, 0x09, 0x01, 0x90, 0x18, 0xe0, 0x06, 0x9a, 0x4d, 0x6f,
0x04, 0x6c, 0xeb, 0xa6, 0x27, 0x92, 0xba, 0xb9, 0x41, 0x0f, 0xae, 0x40, 0x5a, 0xfd, 0xbd, 0x7d,
0x9c, 0x48, 0xfe, 0xe6, 0xab, 0x67, 0x2b, 0x1a, 0x48, 0x00, 0x00, 0x58, 0x1e, 0x09, 0x02, 0x18,
0x15, 0x09, 0x01, 0x19, 0x0d, 0x18, 0x08, 0x0a, 0x02, 0x19, 0x03, 0x18, 0x0d, 0x19, 0x01, 0x0a,
0x03, 0x19, 0x24, 0x09, 0x03, 0x19, 0x15, 0x09, 0x0a, 0x0a, 0x02, 0x09, 0x10, 0x1a, 0x00, 0x0a,
0x07, 0x08, 0x0a, 0x1a, 0x09, 0x09, 0x02, 0x19, 0x08, 0x09, 0x08, 0x0a, 0x2a, 0x19, 0x0a, 0x18,
0x00, 0x19, 0x14, 0x09, 0x0e, 0x1a, 0x0c, 0x09, 0x04, 0x08, 0x13, 0x18, 0x03, 0x08, 0x0a, 0x09,
0x0c, 0x08, 0x2c, 0x09, 0x01, 0x19, 0x0d, 0x09, 0x0d, 0x0a, 0x13, 0x09, 0x02, 0x19, 0x1b, 0x09,
0x01, 0x19, 0x2b, 0x09, 0x01, 0xdf, 0x5d, 0x57, 0xf2, 0xf0, 0xe3, 0xfd, 0x93, 0x18, 0x2c, 0xd9,
0x99, 0xed, 0x61, 0xed, 0x06, 0x2f, 0xa7, 0x4b, 0x5a, 0x25, 0x68, 0x76, 0x20, 0xe3, 0x18, 0x24,
0x24, 0x55, 0x88, 0xe2, 0xb0, 0x00, 0x00, 0xaa, 0x00, 0x09, 0x14, 0x0a, 0x12, 0x09, 0x00, 0x0a,
0x0f, 0x09, 0x00, 0x08, 0x0b, 0x09, 0x00, 0x0a, 0x0a, 0x09, 0x00, 0x08, 0x07, 0x09, 0x00, 0x0a,
0x07, 0x09, 0x00, 0x08, 0x07, 0x09, 0x00, 0x0a, 0x05, 0x09, 0x00, 0x08, 0x05, 0x09, 0x00, 0x0a,
0x05, 0x09, 0x00, 0x08, 0x05, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a,
0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a,
0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a,
0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a,
0x04, 0x09, 0x00, 0x08, 0x04, 0x09, 0x00, 0x0a, 0x04, 0x09, 0x00, 0x08, 0x03, 0x09, 0x04, 0x0a,
0x05, 0x09, 0x04, 0x19, 0x0e, 0x09, 0x1f, 0x19, 0x02, 0x09, 0x0d, 0x0a, 0x10, 0x09, 0x1c, 0x08,
0x05, 0x09, 0x24, 0x19, 0x54, 0x09, 0x15, 0x11, 0x2e, 0x19, 0x1f, 0x09, 0x0d, 0x1a, 0x0f, 0x0a,
0x05, 0x09, 0x09, 0x08, 0x08, 0x09, 0x01, 0x19, 0x09, 0x09, 0x05, 0x19, 0x0b, 0x18, 0x0c, 0x0a,
0x37, 0x09, 0x01, 0x9b, 0x23, 0x5e, 0xc4, 0xdb, 0x44, 0xfe, 0x82, 0x31, 0xc6, 0x04, 0xf2, 0x5d,
0x03, 0x25, 0xb1, 0x77, 0x79, 0x43, 0x68, 0x3f, 0x5a, 0x6f, 0xbf, 0x4e, 0x7d, 0xb6, 0x9e, 0x45,
0x09, 0x4e, 0x98, 0x00, 0x00, 0x3c, 0x4b, 0x09, 0x02, 0x19, 0x22, 0x09, 0x10, 0x08, 0x04, 0x09,
0x00, 0x0a, 0x1f, 0x1a, 0x08, 0x09, 0x14, 0x18, 0x26, 0x09, 0x0a, 0x19, 0x30, 0x09, 0x16, 0x1a,
0x12, 0x09, 0x14, 0x18, 0x00, 0x19, 0x13, 0x09, 0x15, 0x1a, 0x13, 0x09, 0x0a, 0x18, 0x1e, 0x09,
0x15, 0x1a, 0x12, 0x09, 0x0d, 0x19, 0x18, 0x09, 0x10, 0x18, 0x15, 0x09, 0x12, 0x1a, 0x1f, 0x0a,
0x7c, 0x09, 0x01, 0x44, 0x39, 0xe8, 0x75, 0x1b, 0x7f, 0x71, 0x57, 0xa1, 0x4e, 0xd0, 0xc1, 0xeb,
0x61, 0x0d, 0x83, 0xe3, 0x98, 0xe5, 0x7f, 0x1f, 0x72, 0x2f, 0xeb, 0x61, 0xf8, 0x67, 0x0b, 0x9f,
0xd9, 0x81, 0x88, 0x00, 0x00, 0x6a, 0x00, 0x09, 0x06, 0x08, 0x04, 0x18, 0x16, 0x08, 0x0c, 0x09,
0x01, 0x18, 0x00, 0x14, 0x04, 0x18, 0x06, 0x08, 0x19, 0x0a, 0x16, 0x09, 0x05, 0x18, 0x0e, 0x08,
0x0d, 0x09, 0x04, 0x19, 0x12, 0x09, 0x0f, 0x1a, 0x0b, 0x0a, 0x33, 0x09, 0x03, 0x19, 0x13, 0x09,
0x0b, 0x19, 0x17, 0x09, 0x05, 0x19, 0x15, 0x09, 0x00, 0x19, 0x22, 0x09, 0x08, 0x19, 0x09, 0x09,
0x04, 0x0a, 0x0f, 0x18, 0x01, 0x08, 0x1b, 0x19, 0x0b, 0x09, 0x03, 0x08, 0x13, 0x09, 0x11, 0x1a,
0x08, 0x08, 0x12, 0x19, 0x0f, 0x09, 0x03, 0x19, 0x07, 0x09, 0x12, 0x18, 0x0f, 0x19, 0x1a, 0x09,
0x13, 0x0a, 0x0b, 0x1a, 0x02, 0x09, 0x02, 0x05, 0x00, 0x04, 0x10, 0x05, 0x00, 0x15, 0x47, 0x05,
0x00
};
}

View file

@ -0,0 +1,9 @@
#ifndef _skycore__tasdemos__hpp__included__
#define _skycore__tasdemos__hpp__included__
namespace sky
{
extern const unsigned char tasdemos_data[];
}
#endif

View file

@ -0,0 +1,95 @@
#include "tile.hpp"
namespace sky
{
uint16_t _floorheights[6] = {0, 12800, 15360, 17000, 20000, 25000 };
const uint8_t _floor_height[256] = {
0, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5
};
const uint8_t _tunnel_inner[38] = {
16, 16, 16, 16, 15, 14, 13, 11,
8, 7, 6, 5, 3, 3, 3, 3,
3, 3, 2, 1, 0, 0, 0, 0,
0, 0, 1, 2, 3, 3, 3, 3,
3, 3, 5, 6, 7, 8
};
const uint8_t _tunnel_outer[38] = {
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 31, 31, 31, 31, 31, 30, 30,
30, 29, 29, 29, 28, 27, 26, 25,
24, 22, 20, 18, 17, 14
};
int tile::apparent_height() throw()
{
if((_tile & 0xE00) <= 0x400)
return (_tile & 0xF00) >> 9;
else
return -1;
}
bool tile::is_colliding_floor(int16_t hchunk, int16_t vpos)
{
return has_lower_floor() && vpos > 7808 && vpos < 10240;
}
bool tile::is_colliding(int16_t hchunk, int16_t vpos)
{
if(hchunk <= 0)
hchunk = 1 - hchunk;
if(vpos <= 8576 || hchunk > 37)
return false;
//Glitched tiles
if((_tile & 0xE00) >= 0x600)
return true;
//Tunnels.
if((_tile & 0x100) != 0) {
int16_t rvpos = vpos - 8704;
if(rvpos >= 0 && rvpos < (128 * _tunnel_inner[hchunk]))
return false;
}
//Upper limits.
if((_tile & 0xE00) == 0x400 && vpos >= 15360)
return false;
if((_tile & 0xE00) == 0x200 && vpos >= 12800)
return false;
if((_tile & 0xF00) == 0 && vpos >= 10240)
return false;
//The bare turnnel special.
if((_tile & 0xF00) == 0x100) {
int16_t rvpos = vpos - 8704;
if(rvpos >= 0 && rvpos >= (128 * _tunnel_outer[hchunk]))
return false;
}
return true;
}
int16_t tile::upper_level()
{
return _floorheights[_floor_height[_tile >> 8]];
}
unsigned tile::surface_type(int16_t vpos)
{
if(vpos == 10240)
return lower_floor();
else if(vpos == upper_level())
return upper_floor();
else
return 16; //None.
}
bool tile::is_dangerous()
{
if((_tile & 0xF00) == 0)
return ((_tile & 0xF) == 0x00 || (_tile & 0xF) == 0xC);
if((_tile & 0xF00) == 0x100)
return false;
return ((_tile & 0xF0) == 0xC0);
}
bool tile::in_pipe(uint16_t hchunk, int16_t vpos)
{
if(hchunk > 23 || !is_tunnel())
return false;
return (vpos - 8704 < ((_tunnel_inner[hchunk] + _tunnel_outer[hchunk]) / 2) * 128);
}
}

View file

@ -0,0 +1,36 @@
#ifndef _skycore__tile__hpp__included__
#define _skycore__tile__hpp__included__
#include <cstdint>
namespace sky
{
struct tile
{
static const unsigned sticky = 2;
static const unsigned slippery = 8;
static const unsigned suppiles = 9;
static const unsigned boost = 10;
static const unsigned burning = 12;
tile() { _tile = 0; }
tile(uint16_t xtile) { _tile = xtile; }
unsigned lower_floor() { return _tile & 0xF; }
unsigned upper_floor() { return (_tile & 0xF0) >> 4; }
bool is_tunnel() { return ((_tile & 0x100) != 0) && ((_tile & 0xE00) <= 0x400); }
int apparent_height() throw();
bool has_lower_floor() throw() { return ((_tile & 0xF) != 0); }
bool is_colliding_floor(int16_t hchunk, int16_t vpos);
bool is_colliding(int16_t hchunk, int16_t vpos);
int16_t upper_level();
unsigned surface_type(int16_t vpos);
bool is_dangerous();
bool is_block() { return ((_tile & 0xF00) != 0); }
bool is_rblock() { return ((_tile & 0xE00) != 0); }
bool is_blank() { return (_tile == 0); /* Not masked! */ }
bool in_pipe(uint16_t hchunk, int16_t vpos);
uint16_t rawtile() { return _tile; }
private:
uint16_t _tile;
};
}
#endif

View file

@ -0,0 +1,20 @@
#include "util.hpp"
namespace sky
{
vector_input_stream::vector_input_stream(const std::vector<char>& _data, size_t _offset)
: data(_data), offset(_offset)
{
}
vector_input_stream::~vector_input_stream()
{
}
int vector_input_stream::get()
{
if(offset < data.size())
return (uint8_t)data[offset++];
return -1;
}
}

View file

@ -0,0 +1,56 @@
#ifndef _skycore__util__hpp__included__
#define _skycore__util__hpp__included__
#include <cstdint>
#include <vector>
#include "lzs.hpp"
namespace sky
{
inline uint8_t expand_color(uint8_t vgacolor)
{
vgacolor &= 0x3F;
return (vgacolor << 2) | (vgacolor >> 4);
}
inline uint8_t access_array(const std::vector<char>& data, size_t offset)
{
if(offset >= data.size())
throw std::runtime_error("Attempt to access array outside bounds");
return data[offset];
}
inline uint16_t combine(uint8_t a, uint8_t b)
{
return ((uint16_t)b << 8) | (uint16_t)a;
}
inline int sgn(int16_t v)
{
if(v < 0)
return -1;
if(v > 0)
return 1;
return 0;
}
inline int sgn(int32_t v)
{
if(v < 0)
return -1;
if(v > 0)
return 1;
return 0;
}
struct vector_input_stream : public input_stream
{
vector_input_stream(const std::vector<char>& _data, size_t _offset);
~vector_input_stream();
int get();
private:
const std::vector<char>& data;
size_t offset;
};
}
#endif

View file

@ -508,3 +508,4 @@ emucore_callbacks::~emucore_callbacks() throw()
struct emucore_callbacks* ecore_callbacks;
bool new_core_flag = false;
uint32_t magic_flags = 0;