-#ifndef INCLUDED_BADGE2013_MOCKUP_EVENT_H
-#define INCLUDED_BADGE2013_MOCKUP_EVENT_H
+#ifndef INCLUDED_BADGE2013_BADGE_UI_EVENT_H
+#define INCLUDED_BADGE2013_BADGE_UI_EVENT_H
#include <stdint.h>
--- /dev/null
+#!/usr/bin/make -f
+
+CPPFLAGS = $$(pkg-config --cflags gtkmm-3.0) -I. -I ../badge
+CFLAGS = -Wall -Wextra -pedantic -std=c99 -Werror -O0 -g
+CXXFLAGS = -Wall -Wextra -pedantic -std=c++11 -Werror -O0 -g
+
+BADGE = badge
+CONVERTER = level-converter
+
+GLADEFILES = lcd-display.glade
+
+VPATH = ../badge
+
+BADGE_CXXSRCS = mock-main.cc \
+ mock/badge_worker.cc \
+ mock/display.cc \
+ mock/event.cc \
+ mock/lcd-window.cc
+BADGE_CSRCS = badge_main_loop.c \
+ ui/sprite.c \
+ jumpnrun/collision.c \
+ jumpnrun/enemies.c \
+ jumpnrun/items.c \
+ jumpnrun/jumpnrun.c \
+ jumpnrun/tiles.c \
+ jumpnrun/level_load.c
+
+BADGE_CXXOBJS = $(BADGE_CXXSRCS:%.cc=%.o)
+BADGE_COBJS = $(BADGE_CSRCS:%.c=%.o)
+BADGE_OBJS = $(BADGE_CXXOBJS) $(BADGE_COBJS)
+
+CONVERTER_SRCS = tools/level-converter.cc
+CONVERTER_OBJS = $(CONVERTER_SRCS:%.cc=%.o)
+
+CDEPS = $(BADGE_CSRCS:%.c=%.dep)
+CXXDEPS = $(BADGE_CXXSRCS:%.cc=%.dep) $(CONVERTER_SRCS:%.cc=%.dep)
+DEPS = $(CDEPS) $(CXXDEPS)
+
+LDLIBS = $$(pkg-config --libs gtkmm-3.0)
+RM = rm -f
+
+.PHONY: all dep clean distclean play
+
+all: dep $(BADGE) $(CONVERTER)
+
+
+$(BADGE_COBJS) : %.o : %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -c -o $(@:../badge/%=%) $< $(LDLIBS)
+
+$(BADGE_CXXOBJS) : %.o : %.cc
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -c -o $@ $< $(LDLIBS)
+
+$(BADGE): $(BADGE_OBJS)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ $(+:../badge/%=%)
+
+dep: $(DEPS)
+
+$(CDEPS): %.dep : %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -MM $< -MT $(<:%.c=%.o) -MF $@
+
+$(CXXDEPS): %.dep : %.cc
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MM $< -MT $(<:%.cc=%.o) -MF $@
+
+sinclude $(DEPS)
+
+clean:
+ $(RM) $(BADGE_OBJS) $(CONVERTER_OBJS) $(BADGE) $(CONVERTER)
+
+distclean: clean
+ $(RM) $(DEPS) $(wildcard *~)
+
+$(CONVERTER): $(CONVERTER_OBJS)
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ $+
+
+play: all
+ ./$(CONVERTER)
+ ./$(BADGE)
--- /dev/null
+#include "badge_main_loop.h"
+
+#include "jumpnrun/jumpnrun.h"
+#include "ui/display.h"
+#include "ui/event.h"
+#include "ui/sprite.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+void badge_main_loop(void) {
+
+ for(;;) {
+ FILE *fd = fopen("jumpnrun/levels.txt", "r");
+ char buf[12];
+
+ while(fgets(buf, sizeof(buf), fd)) {
+ buf[strlen(buf) - 1] = '\0';
+ char lvname[30];
+ sprintf(lvname, "jumpnrun/%s.lvl", buf);
+ while(jumpnrun_play(lvname) != JUMPNRUN_WON)
+ ;
+ }
+
+ fclose(fd);
+ }
+}
--- /dev/null
+#ifndef INCLUDED_BADGE_MAIN_LOOP_H
+#define INCLUDED_BADGE_MAIN_LOOP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ void badge_main_loop(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#include "mock/lcd-window.hh"
+
+#include <glibmm/thread.h>
+#include <gtkmm/main.h>
+
+#include <memory>
+
+int main(int argc, char *argv[]) {
+ Glib::thread_init();
+
+ Gtk::Main kit(argc, argv);
+ Glib::RefPtr<Gtk::Builder> glade = Gtk::Builder::create();
+ badge2013::lcd_window *win_naked = nullptr;
+
+ glade->add_from_file("mock/lcd-display.glade");
+ glade->get_widget_derived("mainwindow", win_naked);
+
+ std::unique_ptr<badge2013::lcd_window> win(win_naked);
+
+ if(win) {
+ kit.run(*win);
+ }
+}
--- /dev/null
+#include "badge_worker.hh"
+#include "badge_main_loop.h"
+#include "lcd-window.hh"
+
+namespace badge2013 {
+ void badge_framebuffer_set_backend(lcd_window *win);
+
+ badge_worker::badge_worker(lcd_window *win) : win_(win) { }
+
+ void badge_worker::run() {
+ badge_framebuffer_set_backend(win_);
+ badge_main_loop();
+ win_->request_close();
+ }
+}
--- /dev/null
+#ifndef INCLUDED_BADGE2013_MOCKUP_BADGE_WORKER_HH
+#define INCLUDED_BADGE2013_MOCKUP_BADGE_WORKER_HH
+
+namespace badge2013 {
+ class lcd_window;
+
+ class badge_worker {
+ public:
+ badge_worker(lcd_window *win);
+
+ void run();
+
+ private:
+ lcd_window *win_;
+ };
+}
+
+#endif
--- /dev/null
+#include <ui/display.h>
+#include "lcd-window.hh"
+
+namespace badge2013 {
+ namespace {
+ lcd_window *window;
+ }
+
+ void badge_framebuffer_set_backend(lcd_window *win) {
+ window = win;
+ }
+}
+
+void badge_framebuffer_flush(badge_framebuffer const *fb) {
+ badge2013::window->push_framebuffer(*fb);
+}
--- /dev/null
+#include "event.h"
+
+#include <glibmm/threads.h>
+#include <queue>
+
+namespace {
+ Glib::Threads::Mutex badge_event_mutex;
+ Glib::Threads::Cond badge_event_condition;
+ std::queue<badge_event_t> badge_event_queue;
+ uint8_t volatile badge_event_input_state = 0;
+}
+
+uint8_t badge_event_current_input_state(void) {
+ Glib::Threads::Mutex::Lock lock(badge_event_mutex);
+ return badge_event_input_state;
+}
+
+void badge_event_push(badge_event_t e) {
+ Glib::Threads::Mutex::Lock lock(badge_event_mutex);
+
+ badge_event_queue.push(e);
+
+ if(badge_event_type(e) == BADGE_EVENT_USER_INPUT) {
+ badge_event_input_state = badge_event_new_input_state(e);
+ }
+
+ badge_event_condition.signal();
+}
+
+badge_event_t badge_event_wait(void) {
+ Glib::Threads::Mutex::Lock lock(badge_event_mutex);
+
+ while(badge_event_queue.empty()) {
+ badge_event_condition.wait(badge_event_mutex);
+ }
+
+ badge_event_t result = badge_event_queue.front();
+ badge_event_queue.pop();
+
+ return result;
+}
--- /dev/null
+#ifndef INCLUDED_BADGE2013_MOCKUP_EVENT_H
+#define INCLUDED_BADGE2013_MOCKUP_EVENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ui/event.h>
+
+ void badge_event_push(badge_event_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="mainwindow">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">LCD</property>
+ <property name="default_width">144</property>
+ <property name="default_height">98</property>
+ <property name="has_resize_grip">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="canvas">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ </object>
+</interface>
--- /dev/null
+#include "lcd-window.hh"
+#include "badge_worker.hh"
+#include "mock/event.h"
+
+#include <gtkmm/main.h>
+
+namespace badge2013 {
+ lcd_drawingarea::lcd_drawingarea(BaseObjectType *cobject,
+ Glib::RefPtr<Gtk::Builder> const &)
+ : Gtk::DrawingArea(cobject)
+ {
+ sig_redraw_.connect(sigc::mem_fun(*this, &lcd_drawingarea::force_redraw));
+ badge_framebuffer_clear(&framebuffer_);
+ }
+
+ lcd_drawingarea::~lcd_drawingarea() { }
+
+ void lcd_drawingarea::push_framebuffer(badge_framebuffer const &fb) {
+ framebuffer_ = fb;
+ sig_redraw_();
+ }
+
+ void lcd_drawingarea::force_redraw() {
+ Glib::RefPtr<Gdk::Window> win = get_window();
+ if(win) {
+ win->invalidate(true);
+ }
+ }
+
+ bool lcd_drawingarea::on_draw(Cairo::RefPtr<Cairo::Context> const &cr) {
+ Gtk::Allocation alloc = get_allocation();
+ double w_width = alloc.get_width ();
+ double w_height = alloc.get_height();
+
+ double c_width = w_width / BADGE_DISPLAY_WIDTH;
+ double c_height = w_height / BADGE_DISPLAY_HEIGHT;
+
+ cr->save();
+
+ for(int i = 0; i < BADGE_DISPLAY_WIDTH; ++i) {
+ for(int j = 0; j < BADGE_DISPLAY_HEIGHT; ++j) {
+ if(badge_framebuffer_pixel(&framebuffer_, i, j)) {
+ cr->rectangle(c_width * i, c_height * j,
+ c_width , c_height);
+ }
+ }
+ }
+
+ cr->fill();
+ cr->restore();
+
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////
+
+ lcd_window::lcd_window(BaseObjectType *cobject, Glib::RefPtr<Gtk::Builder> const &glade)
+ : Gtk::Window(cobject),
+ worker_(this)
+ {
+ glade->get_widget_derived("canvas", canvas_);
+
+ Glib::signal_timeout().connect(sigc::mem_fun(*this, &lcd_window::on_game_tick ), 11);
+ sig_close_.connect(sigc::mem_fun(*this, &lcd_window::hide));
+ }
+
+ lcd_window::~lcd_window() { }
+
+ void lcd_window::push_framebuffer(badge_framebuffer const &fb) {
+ canvas_->push_framebuffer(fb);
+ }
+
+ void lcd_window::request_close() {
+ sig_close_();
+ }
+
+ bool lcd_window::on_key_press_event (GdkEventKey* event) {
+ uint8_t new_state = key_state_;
+
+ switch(event->keyval) {
+ case GDK_KEY_e: new_state |= BADGE_EVENT_KEY_UP; break;
+ case GDK_KEY_s: new_state |= BADGE_EVENT_KEY_LEFT; break;
+ case GDK_KEY_d: new_state |= BADGE_EVENT_KEY_DOWN; break;
+ case GDK_KEY_f: new_state |= BADGE_EVENT_KEY_RIGHT; break;
+ case GDK_KEY_a: new_state |= BADGE_EVENT_KEY_CENTER; break;
+ case GDK_KEY_j: new_state |= BADGE_EVENT_KEY_BTN_A; break;
+ case GDK_KEY_k: new_state |= BADGE_EVENT_KEY_BTN_B; break;
+ }
+
+ badge_event_push(badge_event_new(BADGE_EVENT_USER_INPUT, key_state_, new_state));
+ key_state_ = new_state;
+
+ return true;
+ }
+
+ bool lcd_window::on_key_release_event(GdkEventKey* event) {
+ uint8_t new_state = key_state_;
+
+ switch(event->keyval) {
+ case GDK_KEY_e: new_state &= ~BADGE_EVENT_KEY_UP; break;
+ case GDK_KEY_s: new_state &= ~BADGE_EVENT_KEY_LEFT; break;
+ case GDK_KEY_d: new_state &= ~BADGE_EVENT_KEY_DOWN; break;
+ case GDK_KEY_f: new_state &= ~BADGE_EVENT_KEY_RIGHT; break;
+ case GDK_KEY_a: new_state &= ~BADGE_EVENT_KEY_CENTER; break;
+ case GDK_KEY_j: new_state &= ~BADGE_EVENT_KEY_BTN_A; break;
+ case GDK_KEY_k: new_state &= ~BADGE_EVENT_KEY_BTN_B; break;
+ }
+
+ badge_event_push(badge_event_new(BADGE_EVENT_USER_INPUT, key_state_, new_state));
+ key_state_ = new_state;
+
+ return true;
+ }
+
+ bool lcd_window::on_game_tick() {
+ badge_event_push(badge_event_new(BADGE_EVENT_GAME_TICK, key_state_, key_state_));
+
+ return true;
+ }
+
+ void lcd_window::on_show() {
+ Gtk::Window::on_show();
+
+ if(!running_) {
+ running_ = true;
+ Glib::Thread::create(sigc::mem_fun(worker_, &badge_worker::run));
+ }
+ }
+}
--- /dev/null
+#ifndef INCLUDED_BADGE2013_MOCKUP_LCD_WINDOW_HH
+#define INCLUDED_BADGE2013_MOCKUP_LCD_WINDOW_HH
+
+#include "badge_worker.hh"
+#include <ui/display.h>
+
+#include <glibmm/dispatcher.h>
+#include <glibmm/thread.h>
+
+#include <gtkmm/builder.h>
+#include <gtkmm/dialog.h>
+#include <gtkmm/drawingarea.h>
+#include <gtkmm/window.h>
+
+#include <cstdint>
+#include <memory>
+
+namespace badge2013 {
+ class lcd_drawingarea : public Gtk::DrawingArea {
+ public:
+ lcd_drawingarea(BaseObjectType *cobject,
+ Glib::RefPtr<Gtk::Builder> const &glade);
+ virtual ~lcd_drawingarea();
+
+ void push_framebuffer(badge_framebuffer const &fb);
+
+ protected:
+ virtual bool on_draw(Cairo::RefPtr<Cairo::Context> const &cr);
+
+ private:
+ void force_redraw();
+
+ Glib::Dispatcher sig_redraw_;
+ badge_framebuffer framebuffer_;
+ };
+
+
+ class lcd_window : public Gtk::Window {
+ public:
+ lcd_window(BaseObjectType *cobject, Glib::RefPtr<Gtk::Builder> const &glade);
+ virtual ~lcd_window();
+
+ void push_framebuffer(badge_framebuffer const &fb);
+ void request_close();
+
+ protected:
+ virtual void on_show();
+ virtual bool on_key_press_event (GdkEventKey* event);
+ virtual bool on_key_release_event(GdkEventKey* event);
+
+ private:
+ bool on_game_tick();
+
+ bool running_ = false;
+ Glib::Dispatcher sig_close_;
+ badge_worker worker_;
+ lcd_drawingarea *canvas_ = 0;
+ std::uint8_t key_state_ = 0;
+ std::uint8_t ticks_ = 0;
+ };
+}
+
+#endif
--- /dev/null
+extern "C" {
+#include <jumpnrun/levels.h>
+#include <jumpnrun/items.h>
+#include <jumpnrun/tiles.h>
+#include <jumpnrun/enemies.h>
+}
+
+#include <boost/spirit/include/qi_symbols.hpp>
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <numeric>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+enum {
+ LEVEL_LINE_COUNT = 13
+};
+
+namespace jnrcpp {
+ struct descriptors {
+ descriptors() {
+ tiles.add
+ ("tube_top_left" , JUMPNRUN_TILE_TYPE_TUBE_TOP_LEFT )
+ ("tube_top_right", JUMPNRUN_TILE_TYPE_TUBE_TOP_RIGHT)
+ ("tube_left" , JUMPNRUN_TILE_TYPE_TUBE_LEFT )
+ ("tube_right" , JUMPNRUN_TILE_TYPE_TUBE_RIGHT )
+ ("brick" , JUMPNRUN_TILE_TYPE_BRICK )
+ ("square" , JUMPNRUN_TILE_TYPE_SQUARE )
+ ;
+
+ items.add
+ ("doc", JUMPNRUN_ITEM_TYPE_DOCUMENT)
+ ;
+
+ enemies.add
+ ("cat" , JUMPNRUN_ENEMY_TYPE_CAT )
+ ("mushroom" , JUMPNRUN_ENEMY_TYPE_MUSHROOM )
+ ("kaninchen", JUMPNRUN_ENEMY_TYPE_KANINCHEN)
+ ;
+ }
+
+ boost::spirit::qi::symbols<char, unsigned> tiles;
+ boost::spirit::qi::symbols<char, unsigned> items;
+ boost::spirit::qi::symbols<char, unsigned> enemies;
+ } const desc;
+
+ struct level_name_map {
+ level_name_map() {
+ std::ifstream in("jumpnrun/levels.txt");
+
+ if(!in) {
+ throw std::logic_error("jumpnrun/levels.txt konnte nicht geöffnet werden.");
+ }
+
+ std::string name;
+ while(std::getline(in, name)) {
+ if(name != "") {
+ names.push_back(name);
+ }
+ }
+ }
+
+ std::vector<std::string> names;
+ } const level_names;
+
+ class level {
+ public:
+ level(std::string const &name)
+ : name(name),
+ level_lines(LEVEL_LINE_COUNT)
+ {
+ std::ifstream in(("jumpnrun/" + name + ".lv").c_str());
+
+ if(!in) {
+ throw std::invalid_argument("Could not open file: " + name + ".lv");
+ }
+
+ for(std::size_t i = 0; i < level_lines.size(); ++i) {
+ std::getline(in, level_lines[i]);
+ }
+
+ std::string line;
+ std::string type_prefix;
+ std::map<char, std::string> *objmap = 0;
+
+ while(std::getline(in, line)) {
+ if(line[0] == '[') {
+ if(line == "[tiles]") {
+ objmap = &tile_types;
+ } else if(line == "[items]") {
+ objmap = &item_types;
+ } else if(line == "[enemies]") {
+ objmap = &enemy_types;
+ } else {
+ throw std::invalid_argument("Unkown type: " + line);
+ }
+ } else if(line != "") {
+ char c;
+ std::string tok;
+ std::istringstream parser(line);
+
+ if(parser >> c >> tok) {
+ if(objmap) {
+ (*objmap)[c] = tok;
+ }
+ } else {
+ throw std::invalid_argument("Line not parseable: " + line);
+ }
+ }
+ }
+ }
+
+ std::size_t length() const {
+ std::size_t level_length = 0;
+
+ for(std::size_t i = 0; i < level_lines.size(); ++i) {
+ level_length = std::max(level_length, level_lines[i].size());
+ }
+
+ return level_length;
+ }
+
+ unsigned find_type(boost::spirit::qi::symbols<char, unsigned> const &types, std::string const &type, std::string const &error) const {
+ unsigned const *p = types.find(type);
+ if(!p) throw std::invalid_argument(error + type);
+ return *p;
+ }
+
+ std::size_t count_things(std::map<char, std::string> const &objmap) const {
+ std::size_t len = length();
+ std::size_t i = 0;
+
+ for(std::size_t x = 0; x < len; ++x) {
+ for(std::size_t y = 0; y < level_lines.size(); ++y) {
+ if(x < level_lines[y].size()) {
+ auto iter = objmap.find(level_lines[y][x]);
+
+ if(iter != objmap.end()) {
+ ++i;
+ }
+ }
+ }
+ }
+
+ return i;
+ }
+
+ std::size_t count_tiles () const { return count_things(tile_types ); }
+ std::size_t count_items () const { return count_things(item_types ); }
+ std::size_t count_enemies() const { return count_things(enemy_types); }
+
+ void dump_things(std::ostream &dest, std::map<char, std::string> const &objmap, boost::spirit::qi::symbols<char, unsigned> const &types, std::string const &error) const {
+ std::size_t len = length();
+
+ for(std::size_t x = 0; x < len; ++x) {
+ for(std::size_t y = 0; y < level_lines.size(); ++y) {
+ if(level_lines[y].size() < x) continue;
+
+ auto iter = objmap.find(level_lines[y][x]);
+
+ if(iter != objmap.end()) {
+ unsigned char buf[3];
+ buf[0] = static_cast<uint8_t>(y) << 4 | static_cast<uint8_t>(x >> 8);
+ buf[1] = static_cast<uint8_t>(x);
+ buf[2] = static_cast<uint8_t>(find_type(types, iter->second, error));
+
+ dest.write(static_cast<char const*>(static_cast<void*>(&buf[0])), sizeof(buf));
+ }
+ }
+ }
+ }
+
+ void dump_tiles (std::ostream &dest) const { dump_things(dest, tile_types, desc.tiles , "Unbekannter Tile-Typ: "); }
+ void dump_items (std::ostream &dest) const { dump_things(dest, item_types, desc.items , "Unbekannter Item-Typ: "); }
+ void dump_enemies(std::ostream &dest) const { dump_things(dest, enemy_types, desc.enemies, "Unbekannter Enemy-Typ: "); }
+
+ std::pair<std::size_t, std::size_t> starting_position() const {
+ std::pair<std::size_t, std::size_t> player_pos(0, 0);
+
+ for(std::size_t y = 0; y < level_lines.size(); ++y) {
+ std::size_t x = level_lines[y].find('P');
+
+ if(x != std::string::npos) {
+ player_pos.first = x;
+ player_pos.second = y;
+ break;
+ }
+ }
+
+ return player_pos;
+ }
+
+ void generate_header(jumpnrun_level *dest) const {
+ dest->header.tile_count = count_tiles ();
+ dest->header.item_count = count_items ();
+ dest->header.enemy_count = count_enemies();
+ }
+
+ void dump(std::ostream &dest) const {
+ jumpnrun_level dummy;
+
+ generate_header(&dummy);
+ uint16_t head[3] = {
+ static_cast<uint16_t>(dummy.header. tile_count),
+ static_cast<uint16_t>(dummy.header. item_count),
+ static_cast<uint16_t>(dummy.header.enemy_count)
+ };
+
+ std::pair<std::size_t, std::size_t> player_pos = starting_position();
+
+ uint16_t spos[2] = {
+ static_cast<uint16_t>(player_pos.first),
+ static_cast<uint16_t>(player_pos.second)
+ };
+
+ dest.write(static_cast<char const *>(static_cast<void const *>(head)), sizeof(head));
+ dest.write(static_cast<char const *>(static_cast<void const *>(spos)), sizeof(spos));
+
+ dump_tiles(dest);
+ dump_items(dest);
+ dump_enemies(dest);
+ }
+
+ private:
+ std::string name;
+ std::vector<std::string> level_lines;
+ std::map<char, std::string> tile_types;
+ std::map<char, std::string> item_types;
+ std::map<char, std::string> enemy_types;
+ };
+}
+
+void jumpnrun_level_dump(size_t level) {
+ std::string name = jnrcpp::level_names.names[level];
+ jnrcpp::level lv(name);
+ std::ofstream out(("jumpnrun/" + name + ".lvl").c_str());
+
+ lv.dump(out);
+}
+
+void jumpnrun_levels_dump(void) {
+ for(size_t i = 0; i < jnrcpp::level_names.names.size(); ++i) {
+ jumpnrun_level_dump(i);
+ }
+}
+
+int main() {
+ jumpnrun_levels_dump();
+}