Compare commits

..

13 Commits

Author SHA1 Message Date
Tommaso Ventafridda c0aa4a1738 ADSB database update (tools, db) (#2701)
* enhance make_airlines_db tool
* enhance make_icao24_db tool
* update airlinescode (.txt, .db), aircraftdatabase/icao24 (.csv, .db)
2025-06-19 17:28:49 +02:00
Tommaso Ventafridda e26f77ee77 Filemanager: go to parent directory keep track of the right selected … (#2702)
* Filemanager: go to parent directory keep track of the right selected item and page number
* review: avoid unnecessary copies in get_extension
2025-06-19 17:24:38 +02:00
plomek f53262783a Improved FPV_ANALOG.txt FREQMAN file (#2700)
* Improved FPV_ANALOG.txt FREQMAN file
Removed unused or super rare analog fpv bands: U, O, H, D
Added 1.2GHz -1.3GHz channels sometimes used for long range analog fpv


* Corrected and updated the labels to be more consistent.

Corrected the labels to be more consistent.

I also somehow messed up the correct channels because 1.3GHz FPV is not fully standardized, but these channels seem to be the most common.
It should be all correct now.

Example transmitters using those channels:
https://greenchip.com.ua/0-0-1615-2.html
https://flymod.net/en/item/walksnail_vtx_9ch
https://pl.aliexpress.com/item/1005006505365351.html
2025-06-19 05:26:02 +00:00
Netro b456c18008 Modified Text Editor to handle long presses. (#2698) 2025-06-19 07:21:54 +02:00
Tommaso Ventafridda fa4b74fd6f Add vendor name in bluetooth rx app (#2696)
* add macaddress db, add vendor name in bluetooth rx app

* show "missing macaddress.db" instead of unknown if db not found

* bluetooth rx list with colors based on mac vendor

* bug fix
2025-06-16 10:57:58 -07:00
RocketGod 18bc2cf11c Made the Dino Game (#2697) 2025-06-15 09:25:10 -07:00
Tommaso Ventafridda 20f28c8331 add dark theme (#2695) 2025-06-13 18:34:20 +02:00
Tommaso Ventafridda ea38a0fe48 Add 1ms hop option to hopper app + 0ms (freeze UI) (#2692) 2025-06-12 11:54:37 +02:00
RocketGod fb2e576b34 Super secret dont look (#2690)
* Add new jammer modes

Overview

This PR enhances the PortaPack Jammer app by introducing eight new signal types, ported from my Flipper Zero RF Jammer app (https://github.com/RocketGod-git/flipper-zero-rf-jammer). These modes expand the app's capability to disrupt a wide range of RF communication protocols, from analog radios to modern digital systems. The implementation preserves the original app structure, resolves namespace conflicts, and ensures compatibility with the Mayhem firmware.

New Modes

The following modes have been added to the options_type in ui_jammer.hpp, with corresponding signal generation in proc_jammer.cpp:

Noise: Generates broadband white noise to interfere with analog and digital signals (e.g., Wi-Fi, Bluetooth, key fobs). Highly effective for overwhelming receivers across a frequency range.

Sine: Produces a continuous, unmodulated sine wave to jam narrowband receivers, ideal for analog FM/AM radios or telemetry systems.

Square: Emits a harmonic-rich square wave, disrupting digital protocols (e.g., OOK, ASK) and systems sensitive to sharp transitions, such as remote keyless entry.

Sawtooth (Experimental): Generates a sawtooth wave with a unique harmonic profile, useful for testing interference against PWM-based or niche analog systems.

Triangle (Experimental): Creates a triangle wave with minimal harmonics, suitable for exploratory jamming of narrowband systems or receiver linearity testing.

Chirp: Outputs a rapid frequency-sweeping chirp signal, effective against frequency-hopping and spread-spectrum systems (e.g., some Wi-Fi, Bluetooth, or military radios).

Gauss: Generates Gaussian noise to mimic natural interference, targeting digital systems like GPS or data links by degrading signal-to-noise ratios.

Brute (Experimental): Transmits a constant maximum-amplitude signal to saturate simple receiver front-ends, useful for brute-force jamming of basic analog devices.

* Super secret

* You gotta get (Get) that (That) dirt off your shoulder
2025-06-10 13:36:26 -07:00
Tommaso Ventafridda 1d79e30d4b Add all jammer modes in hopper app (#2691)
Added all modes that jammer app supports in hopper app.
2025-06-10 06:54:59 -07:00
Totoo be372e12bc Externalize widget (#2688) 2025-06-09 12:52:40 +02:00
Totoo 00853f526a Gfx widget and Radio (#2685)
* widgetize
* gfx and Radio improvement
* format + handle not wfm visual states
* wf or gf
2025-06-07 11:43:07 +02:00
Petro Danylevskyi 37ca7a601c Rename looking glass preset for clarity and consistency (#2686) 2025-06-07 11:27:25 +02:00
48 changed files with 3849 additions and 645 deletions
+62 -1
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2023 TJ Baginski
* Copyright (C) 2025 Tommaso Ventafridda
*
* This file is part of PortaPack.
*
@@ -83,6 +84,50 @@ void reverse_byte_array(uint8_t* arr, int length) {
}
}
MAC_VENDOR_STATUS lookup_mac_vendor_status(const uint8_t* mac_address, std::string& vendor_name) {
static bool db_checked = false;
static bool db_exists = false;
if (!db_checked) {
database db;
database::MacAddressDBRecord dummy_record;
int test_result = db.retrieve_macaddress_record(&dummy_record, "000000");
db_exists = (test_result != DATABASE_NOT_FOUND);
db_checked = true;
}
if (!db_exists) {
vendor_name = "macaddress.db not found";
return MAC_DB_NOT_FOUND;
}
database db;
database::MacAddressDBRecord record;
// Convert MAC address to hex string
std::string mac_hex = "";
for (int i = 0; i < 3; i++) {
// Only need first 3 bytes for OUI
mac_hex += to_string_hex(mac_address[i], 2);
}
int result = db.retrieve_macaddress_record(&record, mac_hex);
if (result == DATABASE_RECORD_FOUND) {
vendor_name = std::string(record.vendor_name);
return MAC_VENDOR_FOUND;
} else {
vendor_name = "Unknown";
return MAC_VENDOR_NOT_FOUND;
}
}
std::string lookup_mac_vendor(const uint8_t* mac_address) {
std::string vendor_name;
lookup_mac_vendor_status(mac_address, vendor_name);
return vendor_name;
}
namespace ui {
std::string pdu_type_to_string(ADV_PDU_TYPE type) {
@@ -165,7 +210,10 @@ void RecentEntriesTable<BleRecentEntries>::draw(
line += pad_string_with_spaces(db_spacing) + dbStr;
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, line);
Style row_style = (entry.vendor_status == MAC_VENDOR_FOUND) ? style : Style{style.font, style.background, Color::grey()};
painter.draw_string(target_rect.location(), row_style, line);
}
BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const BleRecentEntry& entry)
@@ -178,10 +226,14 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
&text_mac_address,
&label_pdu_type,
&text_pdu_type,
&label_vendor,
&text_vendor,
&labels});
text_mac_address.set(to_string_mac_address(entry.packetData.macAddress, 6, false));
text_pdu_type.set(pdu_type_to_string(entry.pduType));
std::string vendor_name = lookup_mac_vendor(entry.packetData.macAddress);
text_vendor.set(vendor_name);
button_done.on_select = [&nav](const ui::Button&) {
nav.pop();
@@ -370,6 +422,10 @@ void BleRecentEntryDetailView::paint(Painter& painter) {
void BleRecentEntryDetailView::set_entry(const BleRecentEntry& entry) {
entry_ = entry;
text_mac_address.set(to_string_mac_address(entry.packetData.macAddress, 6, false));
text_pdu_type.set(pdu_type_to_string(entry.pduType));
std::string vendor_name = lookup_mac_vendor(entry.packetData.macAddress);
text_vendor.set(vendor_name);
set_dirty();
}
@@ -952,6 +1008,11 @@ void BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
entry.pduType = pdu_type;
entry.channelNumber = channel_number;
if (entry.vendor_status == MAC_VENDOR_UNKNOWN) {
std::string vendor_name;
entry.vendor_status = lookup_mac_vendor_status(entry.packetData.macAddress, vendor_name);
}
// Parse Data Section into buffer to be interpretted later.
for (int i = 0; i < packet->dataLen; i++) {
entry.packetData.data[i] = packet->data[i];
+18
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2023 TJ Baginski
* Copyright (C) 2025 Tommaso Ventafridda
*
* This file is part of PortaPack.
*
@@ -33,6 +34,7 @@
#include "ui_record_view.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "database.hpp"
#include "log_file.hpp"
#include "utility.hpp"
#include "usb_serial_thread.hpp"
@@ -72,6 +74,13 @@ typedef enum {
RESERVED8 = 15
} ADV_PDU_TYPE;
typedef enum {
MAC_VENDOR_UNKNOWN = 0,
MAC_VENDOR_FOUND = 1,
MAC_VENDOR_NOT_FOUND = 2,
MAC_DB_NOT_FOUND = 3
} MAC_VENDOR_STATUS;
struct BleRecentEntry {
using Key = uint64_t;
@@ -87,6 +96,7 @@ struct BleRecentEntry {
uint16_t numHits;
ADV_PDU_TYPE pduType;
uint8_t channelNumber;
MAC_VENDOR_STATUS vendor_status;
bool entryFound;
BleRecentEntry()
@@ -105,6 +115,7 @@ struct BleRecentEntry {
numHits{},
pduType{},
channelNumber{},
vendor_status{MAC_VENDOR_UNKNOWN},
entryFound{} {
}
@@ -152,6 +163,13 @@ class BleRecentEntryDetailView : public View {
{9 * 8, 1 * 16, 17 * 8, 16},
"-"};
Labels label_vendor{
{{0 * 8, 2 * 16}, "Vendor:", Theme::getInstance()->fg_light->foreground}};
Text text_vendor{
{7 * 8, 2 * 16, 23 * 8, 16},
"-"};
Labels labels{
{{0 * 8, 3 * 16}, "Len", Theme::getInstance()->fg_light->foreground},
{{5 * 8, 3 * 16}, "Type", Theme::getInstance()->fg_light->foreground},
+20 -3
View File
@@ -319,7 +319,8 @@ void BLETxView::on_tx_progress(const bool done) {
BLETxView::BLETxView(NavigationView& nav)
: nav_{nav} {
add_children({&button_open,
add_children({&dataEditView,
&button_open,
&text_filename,
&progressbar,
&check_rand_mac,
@@ -340,7 +341,6 @@ BLETxView::BLETxView(NavigationView& nav)
&label_mac_address,
&text_mac_address,
&label_data_packet,
&dataEditView,
&button_clear_marked,
&button_save_packet,
&button_switch});
@@ -430,6 +430,23 @@ BLETxView::BLETxView(NavigationView& nav)
}
};
dataEditView.on_change = [this](uint8_t value) {
// Reject setting newline at index 29.
if (cursor_pos.col != 29) {
uint16_t dataBytePos = (cursor_pos.line * 29) + cursor_pos.col;
packets[current_packet].advertisementData[dataBytePos] = uint_to_char(value, 16);
update_current_packet(packets[current_packet], current_packet);
}
};
dataEditView.on_cursor_moved = [this]() {
// Save last selected cursor.
cursor_pos.line = dataEditView.line();
cursor_pos.col = dataEditView.col();
};
button_clear_marked.on_select = [this](Button&) {
marked_counter = 0;
markedBytes.clear();
@@ -531,7 +548,6 @@ void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex)
for (const std::string& str : strings) {
dataFile.write(str.c_str(), str.size());
dataFile.write("\n", 1);
;
}
dataFile.~File();
@@ -546,6 +562,7 @@ void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex)
dataEditView.set_font_zoom(true);
dataEditView.set_file(*dataFileWrapper);
dataEditView.redraw(true, true);
dataEditView.cursor_set(cursor_pos.line, cursor_pos.col);
}
void BLETxView::set_parent_rect(const Rect new_parent_rect) {
-3
View File
@@ -287,9 +287,6 @@ class BLETxView : public View {
Labels label_data_packet{
{{0 * 8, 9 * 16}, "Packet Data:", Theme::getInstance()->fg_light->foreground}};
Console console{
{0, 9 * 18, screen_width, screen_height - 80}};
TextViewer dataEditView{
{0, 9 * 18, screen_width, screen_height - 80}};
@@ -34,6 +34,7 @@ constexpr std::string_view mayhem_information_list[] = {
"RedFox-Fr,nemanjan00,",
"MichalLeonBorsuk,",
"MatiasFernandez,Giorgiofox",
"TommasoVentafridda",
" ",
"#Havoc:",
"jboone,furrtek,eried,argilo,",
+64 -90
View File
@@ -228,6 +228,8 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24));
// Collect all entries first
std::list<fileman_entry> all_entries;
for (const auto& entry : fs::directory_iterator(dir_path, u"*")) {
// Hide files starting with '.' (hidden / tmp).
if (!show_hidden_files && is_hidden_file(entry.path()))
@@ -235,36 +237,40 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path())))
insert_sorted(entry_list, {entry.path().string(), (uint32_t)entry.size(), false});
insert_sorted(all_entries, {entry.path().string(), (uint32_t)entry.size(), false});
} else if (fs::is_directory(entry.status())) {
insert_sorted(entry_list, {entry.path().string(), 0, true});
insert_sorted(all_entries, {entry.path().string(), 0, true});
}
}
// paginating
auto list_size = entry_list.size();
nb_pages = 1 + (list_size / items_per_page);
size_t start = pagination * items_per_page;
size_t stop = start + items_per_page;
if (list_size > start) {
if (list_size < stop)
stop = list_size;
entry_list.erase(std::next(entry_list.begin(), stop), entry_list.end());
entry_list.erase(entry_list.begin(), std::next(entry_list.begin(), start));
// Calculate pagination
nb_pages = (all_entries.size() + items_per_page - 1) / items_per_page;
if (nb_pages == 0) nb_pages = 1;
size_t start_idx = pagination * items_per_page;
size_t end_idx = std::min(start_idx + items_per_page, all_entries.size());
// Add "parent" directory if not at the root and on first page
if (!dir_path.empty() && pagination == 0) {
entry_list.push_back({parent_dir_path.string(), 0, true});
}
// Add "parent" directory if not at the root.
if (!dir_path.empty() && pagination == 0)
entry_list.insert(entry_list.begin(), {parent_dir_path.string(), 0, true});
// add next page
if (list_size > start + items_per_page) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
}
// add prev page
// Add prev page navigation if not on first page
if (pagination > 0) {
entry_list.insert(entry_list.begin(), {str_back, (uint32_t)pagination - 1, true});
entry_list.push_back({str_back, (uint32_t)pagination - 1, true});
}
// Add entries for current page
auto it = all_entries.begin();
std::advance(it, start_idx);
for (size_t i = start_idx; i < end_idx && it != all_entries.end(); i++, ++it) {
entry_list.push_back(*it);
}
// Add next page navigation if not on last page
if (end_idx < all_entries.size()) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
}
}
@@ -320,30 +326,49 @@ void FileManBaseView::focus() {
} else {
menu_view.focus();
}
// Set menu to the correct page and select the correct item
menu_view.set_highlighted(prev_highlight % items_per_page);
}
// Push directory - store the global index (page * items_per_page + local_index)
void FileManBaseView::push_dir(const fs::path& path) {
if (path == parent_dir_path) {
pop_dir();
} else {
// Save global index (combines page number and item position)
saved_index_stack.push_back(menu_view.highlighted_index() + (pagination * items_per_page));
current_path /= path;
saved_index_stack.push_back(menu_view.highlighted_index());
menu_view.set_highlighted(0);
reload_current(true);
reload_current(true); // Reset pagination when entering new directory
}
}
void FileManBaseView::pop_dir() {
if (saved_index_stack.empty())
if (current_path.empty()) {
return;
}
// Move to parent directory
current_path = current_path.parent_path();
reload_current(true);
menu_view.set_highlighted(saved_index_stack.back());
saved_index_stack.pop_back();
// Restore the previous global index if available
if (!saved_index_stack.empty()) {
uint32_t global_index = saved_index_stack.back();
saved_index_stack.pop_back();
// Calculate pagination from global index
pagination = global_index / items_per_page;
// Calculate local index within the page
prev_highlight = global_index % items_per_page;
restoring_navigation = true;
}
reload_current(false); // Important: don't reset pagination
}
std::string get_extension(std::string t) {
std::string FileManBaseView::get_extension(const std::string& t) const {
const auto index = t.find_last_of(u'.');
if (index == t.npos) {
return {};
@@ -372,7 +397,9 @@ void FileManBaseView::refresh_list() {
if (on_refresh_widgets)
on_refresh_widgets(false);
prev_highlight = menu_view.highlighted_index();
if (!restoring_navigation) {
prev_highlight = menu_view.highlighted_index();
}
menu_view.clear();
for (const auto& entry : entry_list) {
@@ -411,10 +438,14 @@ void FileManBaseView::refresh_list() {
}
menu_view.set_highlighted(prev_highlight);
restoring_navigation = false;
}
void FileManBaseView::reload_current(bool reset_pagination) {
if (reset_pagination) pagination = 0;
// Only reset pagination if explicitly requested
if (reset_pagination) {
pagination = 0;
}
load_directory_contents(current_path);
refresh_list();
}
@@ -489,63 +520,6 @@ void FileLoadView::refresh_widgets(const bool) {
set_dirty();
}
/* FileSaveView **************************************************************/
/*
FileSaveView::FileSaveView(
NavigationView& nav,
const fs::path& path,
const fs::path& file
) : nav_{ nav },
path_{ path },
file_{ file }
{
add_children({
&text_path,
&button_edit_path,
&text_name,
&button_edit_name,
&button_save,
&button_cancel,
});
button_edit_path.on_select = [this](Button&) {
buffer_ = path_.string();
text_prompt(nav_, buffer_, max_filename_length,ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string&) {
path_ = buffer_;
refresh_widgets();
});
};
button_edit_name.on_select = [this](Button&) {
buffer_ = file_.string();
text_prompt(nav_, buffer_, max_filename_length,ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string&) {
file_ = buffer_;
refresh_widgets();
});
};
button_save.on_select = [this](Button&) {
if (on_save)
on_save(path_ / file_);
else
nav_.pop();
};
button_cancel.on_select = [this](Button&) {
nav_.pop();
};
refresh_widgets();
}
void FileSaveView::refresh_widgets() {
text_path.set(truncate(path_, 30));
text_name.set(truncate(file_, 30));
set_dirty();
}
*/
/* FileManagerView ***********************************************************/
void FileManagerView::refresh_widgets(const bool v) {
+2
View File
@@ -64,6 +64,7 @@ class FileManBaseView : public View {
uint32_t prev_highlight = 0;
uint8_t pagination = 0;
uint8_t nb_pages = 1;
bool restoring_navigation = false;
static constexpr size_t max_filename_length = 20;
static constexpr size_t max_items_loaded = 75; // too memory hungry, so won't sort it
static constexpr size_t items_per_page = 20;
@@ -96,6 +97,7 @@ class FileManBaseView : public View {
void load_directory_contents(const std::filesystem::path& dir_path);
void load_directory_contents_unordered(const std::filesystem::path& dir_path, size_t file_cnt);
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
std::string get_extension(const std::string& t) const;
void copy_waterfall(std::filesystem::path path);
NavigationView& nav_;
@@ -954,6 +954,7 @@ class SetThemeView : public View {
{"Aqua", 2},
{"Green", 3},
{"Red", 4},
{"Dark", 5},
},
true};
+97 -19
View File
@@ -26,6 +26,7 @@
#include "log_file.hpp"
#include "string_format.hpp"
#include "irq_controls.hpp"
using namespace portapack;
namespace fs = std::filesystem;
@@ -81,21 +82,75 @@ void TextViewer::paint(Painter& painter) {
paint_cursor(painter);
}
void TextViewer::enable_long_press() {
// Enable long press on "Select".
SwitchesState config;
config[toUType(Switch::Sel)] = true;
set_switches_long_press_config(config);
}
void TextViewer::on_focus() {
enable_long_press();
}
bool TextViewer::on_key(const KeyEvent key) {
int16_t delta_col = 0;
int16_t delta_line = 0;
if (key == KeyEvent::Left)
delta_col = -1;
else if (key == KeyEvent::Right)
delta_col = 1;
else if (key == KeyEvent::Up)
delta_line = -1;
else if (key == KeyEvent::Down)
delta_line = 1;
else if (key == KeyEvent::Select && on_select) {
on_select();
return true;
if (key == KeyEvent::Select) {
// Toggle 'digit' mode with long-press.
if (digit_mode_ || key_is_long_pressed(key)) {
digit_mode_ = !digit_mode_;
set_dirty();
return true;
}
}
if (digit_mode_) {
switch (key) {
case KeyEvent::Left:
delta_col = -1;
break;
case KeyEvent::Right:
delta_col = 1;
break;
case KeyEvent::Up:
set_value(value_ == 0x0F ? 0x00 : value_ + 1);
break;
case KeyEvent::Down:
set_value(value_ == 0x00 ? 0x0F : value_ - 1);
break;
default:
return false;
}
if (delta_col == 0 && delta_line == 0) {
return true;
}
} else {
switch (key) {
case KeyEvent::Left:
delta_col = -1;
break;
case KeyEvent::Right:
delta_col = 1;
break;
case KeyEvent::Up:
delta_line = -1;
break;
case KeyEvent::Down:
delta_line = 1;
break;
case KeyEvent::Select: {
if (on_select) {
on_select();
return true;
}
break;
}
default:
return false;
}
}
// Always allow cursor direction to be updated.
@@ -111,19 +166,42 @@ bool TextViewer::on_key(const KeyEvent key) {
bool TextViewer::on_encoder(EncoderEvent delta) {
bool updated = false;
if (cursor_.dir == ScrollDirection::Horizontal)
updated = apply_scrolling_constraints(0, delta);
else {
delta *= 16;
updated = apply_scrolling_constraints(delta, 0);
if (digit_mode_) {
if (delta > 0) {
set_value(value_ == 0x0F ? 0x00 : value_ + 1);
} else if (delta < 0) {
set_value(value_ == 0x00 ? 0x0F : value_ - 1);
}
updated = true;
} else {
if (cursor_.dir == ScrollDirection::Horizontal) {
updated = apply_scrolling_constraints(0, delta);
} else {
delta *= 16;
updated = apply_scrolling_constraints(delta, 0);
}
if (updated) {
redraw();
}
}
if (updated)
redraw();
return updated;
}
void TextViewer::set_value(uint8_t new_value) {
if (new_value != value_) {
value_ = new_value;
if (on_change) {
on_change(value_);
}
set_dirty();
}
}
void TextViewer::redraw(bool redraw_text, bool redraw_marked) {
paint_state_.redraw_text = redraw_text;
paint_state_.redraw_marked = redraw_marked;
@@ -54,10 +54,13 @@ class TextViewer : public Widget {
std::function<void()> on_select{};
std::function<void()> on_cursor_moved{};
std::function<void(uint8_t)> on_change{};
void paint(Painter& painter) override;
bool on_key(KeyEvent key) override;
bool on_encoder(EncoderEvent delta) override;
void set_value(uint8_t new_value);
void on_focus() override;
void redraw(bool redraw_text = false, bool redraw_marked = false);
@@ -74,6 +77,7 @@ class TextViewer : public Widget {
void cursor_set(uint16_t line, uint16_t col);
void cursor_mark_selected();
void cursor_clear_marked();
void enable_long_press();
typedef std::pair<uint16_t, uint16_t> LineColPair;
std::vector<LineColPair> lineColPair{};
@@ -95,6 +99,9 @@ class TextViewer : public Widget {
int8_t char_height{};
uint8_t max_line{};
uint8_t max_col{};
bool digit_mode_{false};
uint8_t value_{0};
bool allow_digit_mode_{true};
/* Returns true if the cursor was updated. */
bool apply_scrolling_constraints(
+11
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2022 Arjan Onwezen
* Copyright (C) 2025 Tommaso Ventafridda
*
* This file is part of PortaPack.
*
@@ -56,6 +57,16 @@ int database::retrieve_aircraft_record(AircraftDBRecord* record, std::string sea
return (result);
}
int database::retrieve_macaddress_record(MacAddressDBRecord* record, std::string search_term) {
file_path = macaddress_dir / u"macaddress.db";
index_item_length = 7;
record_length = 64;
result = retrieve_record(file_path, index_item_length, record_length, record, search_term);
return (result);
}
int database::retrieve_record(std::filesystem::path file_path, int index_item_length, int record_length, void* record, std::string search_term) {
if (search_term.empty())
return DATABASE_RECORD_NOT_FOUND;
+8 -1
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2022 Arjan Onwezen
* Copyright (C) 2025 Tommaso Ventafridda
*
* This file is part of PortaPack.
*
@@ -60,6 +61,12 @@ class database {
int retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term);
struct MacAddressDBRecord {
char vendor_name[64]; // vendor/manufacturer name
};
int retrieve_macaddress_record(MacAddressDBRecord* record, std::string search_term);
private:
std::filesystem::path file_path = ""; // path including filename
int index_item_length = 0; // length of index item
@@ -77,4 +84,4 @@ class database {
int retrieve_record(std::filesystem::path file_path, int index_item_length, int record_length, void* record, std::string search_term);
};
#endif /*__DATABASE_H__*/
#endif /*__DATABASE_H__*/
+75
View File
@@ -0,0 +1,75 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
/*
* Chrome Dino Game for Portapack Mayhem
* Based on the original DinoGame by various contributors
*/
#include "ui.hpp"
#include "ui_dinogame.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::dinogame {
void initialize_app(ui::NavigationView& nav) {
nav.push<DinoGameView>();
}
} // namespace ui::external_app::dinogame
extern "C" {
__attribute__((section(".external_app.app_dinogame.application_information"), used)) application_information_t _application_information_dinogame = {
(uint8_t*)0x00000000,
ui::external_app::dinogame::initialize_app,
CURRENT_HEADER_VERSION,
VERSION_MD5,
"Dino Game",
{
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xF0,
0xF8,
0xFC,
0xFE,
0xFF,
0xFF,
0x7F,
0x7F,
0x3F,
0x3F,
0x3E,
0x3C,
0x38,
0x30,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
},
ui::Color::green().v,
app_location_t::GAMES,
-1,
{0, 0, 0, 0},
0x00000000,
};
}
+447
View File
@@ -0,0 +1,447 @@
/*
standing - 34x36
ducking - 46x24
*/
const uint16_t dino_default[] PROGMEM = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0010 (16)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0020 (32)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0030 (48)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0040 (64)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0050 (80)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0060 (96)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0070 (112)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0080 (128)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0090 (144)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x00A0 (160)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00B0 (176)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00C0 (192)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00D0 (208)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x00E0 (224)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00F0 (240)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0100 (256)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x0110 (272)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0120 (288)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0130 (304)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0140 (320)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0150 (336)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0160 (352)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0170 (368)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0180 (384)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0190 (400)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01A0 (416)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01B0 (432)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01C0 (448)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01D0 (464)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x01E0 (480)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01F0 (496)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0200 (512)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0210 (528)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0220 (544)
0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0230 (560)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0240 (576)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0250 (592)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0260 (608)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0270 (624)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0280 (640)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0290 (656)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02A0 (672)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02B0 (688)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x02C0 (704)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02D0 (720)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02E0 (736)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x02F0 (752)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0300 (768)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0310 (784)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0320 (800)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0330 (816)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0340 (832)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0350 (848)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0360 (864)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0370 (880)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0380 (896)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0390 (912)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x03A0 (928)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03B0 (944)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03C0 (960)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03D0 (976)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03E0 (992)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03F0 (1008)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0400 (1024)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0410 (1040)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0420 (1056)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0430 (1072)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0440 (1088)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0450 (1104)
0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0460 (1120)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0470 (1136)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0480 (1152)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0490 (1168)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04A0 (1184)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04B0 (1200)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04C0 (1216)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
const uint16_t dino_leftstep[] PROGMEM = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0010 (16)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0020 (32)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0030 (48)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0040 (64)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0050 (80)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0060 (96)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0070 (112)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0080 (128)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0090 (144)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x00A0 (160)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00B0 (176)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00C0 (192)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00D0 (208)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x00E0 (224)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00F0 (240)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0100 (256)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x0110 (272)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0120 (288)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0130 (304)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0140 (320)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0150 (336)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0160 (352)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0170 (368)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0180 (384)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0190 (400)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01A0 (416)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01B0 (432)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01C0 (448)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01D0 (464)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x01E0 (480)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01F0 (496)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0200 (512)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0210 (528)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0220 (544)
0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0230 (560)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0240 (576)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0250 (592)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0260 (608)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0270 (624)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0280 (640)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0290 (656)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02A0 (672)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02B0 (688)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x02C0 (704)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02D0 (720)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02E0 (736)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x02F0 (752)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0300 (768)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0310 (784)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0320 (800)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0330 (816)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0340 (832)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0350 (848)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0360 (864)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0370 (880)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0380 (896)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0390 (912)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x03A0 (928)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03B0 (944)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03C0 (960)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03D0 (976)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03E0 (992)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03F0 (1008)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0400 (1024)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0410 (1040)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0420 (1056)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0430 (1072)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0440 (1088)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0450 (1104)
0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0460 (1120)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0470 (1136)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0480 (1152)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0490 (1168)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04A0 (1184)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04B0 (1200)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04C0 (1216)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
const uint16_t dino_rightstep[] PROGMEM = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0010 (16)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0020 (32)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0030 (48)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0040 (64)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0050 (80)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0060 (96)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0070 (112)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0080 (128)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0090 (144)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x00A0 (160)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00B0 (176)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00C0 (192)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00D0 (208)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x00E0 (224)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00F0 (240)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0100 (256)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x0110 (272)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0120 (288)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0130 (304)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0140 (320)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0150 (336)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0160 (352)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0170 (368)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0180 (384)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0190 (400)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01A0 (416)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01B0 (432)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01C0 (448)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01D0 (464)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x01E0 (480)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01F0 (496)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0200 (512)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0210 (528)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0220 (544)
0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0230 (560)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0240 (576)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0250 (592)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0260 (608)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0270 (624)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0280 (640)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0290 (656)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02A0 (672)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02B0 (688)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x02C0 (704)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02D0 (720)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02E0 (736)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x02F0 (752)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0300 (768)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0310 (784)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0320 (800)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0330 (816)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0340 (832)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0350 (848)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0360 (864)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0370 (880)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0380 (896)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0390 (912)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x03A0 (928)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03B0 (944)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03C0 (960)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03D0 (976)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03E0 (992)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03F0 (1008)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0400 (1024)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0410 (1040)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0420 (1056)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0430 (1072)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0440 (1088)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0450 (1104)
0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0460 (1120)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0470 (1136)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0480 (1152)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0490 (1168)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04A0 (1184)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04B0 (1200)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04C0 (1216)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
const uint16_t dino_ducking_leftstep[] PROGMEM = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0010 (16)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0020 (32)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0030 (48)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0040 (64)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0050 (80)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x0060 (96)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0070 (112)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0080 (128)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0090 (144)
0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00A0 (160)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00B0 (176)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00C0 (192)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00D0 (208)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00E0 (224)
0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00F0 (240)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0100 (256)
0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0110 (272)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0120 (288)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0130 (304)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x0140 (320)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0150 (336)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0160 (352)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0170 (368)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0180 (384)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0190 (400)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01A0 (416)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01B0 (432)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01C0 (448)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01D0 (464)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01E0 (480)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01F0 (496)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0200 (512)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0210 (528)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0220 (544)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0230 (560)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0240 (576)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0250 (592)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0260 (608)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0270 (624)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0280 (640)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0290 (656)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02A0 (672)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x02B0 (688)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02C0 (704)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02D0 (720)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x02E0 (736)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02F0 (752)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0300 (768)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0310 (784)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0320 (800)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0330 (816)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0340 (832)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0350 (848)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0360 (864)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0370 (880)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0380 (896)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0390 (912)
0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03A0 (928)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03B0 (944)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x03C0 (960)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03D0 (976)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
const uint16_t dino_ducking_rightstep[] PROGMEM = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0010 (16)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0020 (32)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0030 (48)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0040 (64)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0050 (80)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x0060 (96)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0070 (112)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0080 (128)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0090 (144)
0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00A0 (160)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00B0 (176)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00C0 (192)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00D0 (208)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00E0 (224)
0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x00F0 (240)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0100 (256)
0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0110 (272)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0120 (288)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0130 (304)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x0140 (320)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0150 (336)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0160 (352)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0170 (368)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0180 (384)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0190 (400)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01A0 (416)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01B0 (432)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01C0 (448)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01D0 (464)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01E0 (480)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01F0 (496)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0200 (512)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0210 (528)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0220 (544)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0230 (560)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0240 (576)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0250 (592)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0260 (608)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0270 (624)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0280 (640)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0290 (656)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02A0 (672)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02B0 (688)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02C0 (704)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02D0 (720)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x02E0 (736)
0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02F0 (752)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0300 (768)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x0310 (784)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0320 (800)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0330 (816)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0340 (832)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0350 (848)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0360 (864)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0370 (880)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0380 (896)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0390 (912)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03A0 (928)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03B0 (944)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03C0 (960)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03D0 (976)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
const uint16_t dino_gameover[] PROGMEM = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0010 (16)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0020 (32)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0030 (48)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0040 (64)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0050 (80)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0060 (96)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0070 (112)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0080 (128)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0090 (144)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0x528A, 0xFFFF, 0x528A, // 0x00A0 (160)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00B0 (176)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x00C0 (192)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00D0 (208)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x00E0 (224)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00F0 (240)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0100 (256)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x0110 (272)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0120 (288)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0130 (304)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0140 (320)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0150 (336)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0160 (352)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0170 (368)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0180 (384)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0190 (400)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01A0 (416)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01B0 (432)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01C0 (448)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01D0 (464)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x01E0 (480)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01F0 (496)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0200 (512)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0210 (528)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0220 (544)
0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0230 (560)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0240 (576)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0250 (592)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0260 (608)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0270 (624)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0280 (640)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0290 (656)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02A0 (672)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02B0 (688)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x02C0 (704)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02D0 (720)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x02E0 (736)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x02F0 (752)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0300 (768)
0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0310 (784)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0320 (800)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0330 (816)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0340 (832)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0350 (848)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0360 (864)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0370 (880)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0380 (896)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0390 (912)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x03A0 (928)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03B0 (944)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03C0 (960)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03D0 (976)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03E0 (992)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, // 0x03F0 (1008)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0400 (1024)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0410 (1040)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0420 (1056)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0430 (1072)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0440 (1088)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0450 (1104)
0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0460 (1120)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0470 (1136)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0480 (1152)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0490 (1168)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04A0 (1184)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04B0 (1200)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x04C0 (1216)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
@@ -0,0 +1,121 @@
// 34x27
const uint16_t pterodactyl_upflop[] PROGMEM = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0xFFFF, // 0x0010 (16)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0020 (32)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0030 (48)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0040 (64)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0050 (80)
0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0060 (96)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0070 (112)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0080 (128)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x0090 (144)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00A0 (160)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x00B0 (176)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00C0 (192)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x00D0 (208)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, // 0x00E0 (224)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00F0 (240)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0100 (256)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0110 (272)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0120 (288)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0130 (304)
0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0140 (320)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0150 (336)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0160 (352)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0170 (368)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0180 (384)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0190 (400)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01A0 (416)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01B0 (432)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01C0 (448)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01D0 (464)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01E0 (480)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01F0 (496)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0200 (512)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0210 (528)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0220 (544)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0230 (560)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0240 (576)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0250 (592)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0260 (608)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0270 (624)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0280 (640)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0290 (656)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02A0 (672)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02B0 (688)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02C0 (704)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02D0 (720)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02E0 (736)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02F0 (752)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0300 (768)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0310 (784)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0320 (800)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0330 (816)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0340 (832)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0350 (848)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0360 (864)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0370 (880)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0380 (896)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0390 (912)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
const uint16_t pterodactyl_downflop[] PROGMEM = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0010 (16)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0020 (32)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0030 (48)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0040 (64)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0050 (80)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0060 (96)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0070 (112)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0080 (128)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x0090 (144)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00A0 (160)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, // 0x00B0 (176)
0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00C0 (192)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x00D0 (208)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00E0 (224)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x00F0 (240)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0100 (256)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0110 (272)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0120 (288)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0130 (304)
0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0140 (320)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0150 (336)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0160 (352)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0170 (368)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0180 (384)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0190 (400)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01A0 (416)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01B0 (432)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01C0 (448)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01D0 (464)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x01E0 (480)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x01F0 (496)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0200 (512)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0210 (528)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0220 (544)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0230 (560)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0240 (576)
0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0250 (592)
0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0260 (608)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0270 (624)
0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, // 0x0280 (640)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0290 (656)
0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02A0 (672)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02B0 (688)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02C0 (704)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02D0 (720)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02E0 (736)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x02F0 (752)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0300 (768)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0310 (784)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, 0xFFFF, 0xFFFF, // 0x0320 (800)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0330 (816)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, 0x528A, 0x528A, // 0x0340 (832)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0350 (848)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x528A, // 0x0360 (864)
0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0370 (880)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0380 (896)
0xFFFF, 0x528A, 0x528A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // 0x0390 (912)
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+808
View File
@@ -0,0 +1,808 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
/*
* Chrome Dino Game for Portapack Mayhem
* Based on the original DinoGame by various contributors
*/
#include "ui_dinogame.hpp"
namespace ui::external_app::dinogame {
#define PROGMEM
#include "sprites/dino.h"
#include "sprites/pterodactyl.h"
#undef PROGMEM
// Global variables
Ticker game_timer;
Painter painter;
static Callback game_update_callback = nullptr;
static uint32_t game_update_timeout = 0;
static uint32_t game_update_counter = 0;
static DinoGameView* current_instance = nullptr;
const Color pp_colors[] = {
Color::white(),
Color::blue(),
Color::yellow(),
Color::purple(),
Color::green(),
Color::red(),
Color::magenta(),
Color::orange(),
Color::black(),
};
// Drawing functions
void cls() {
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
}
void fillrect(int x1, int y1, int x2, int y2, int color) {
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
}
void rect(int x1, int y1, int x2, int y2, int color) {
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
}
// Timer implementation
void check_game_timer() {
if (game_update_callback) {
if (++game_update_counter >= game_update_timeout) {
game_update_counter = 0;
game_update_callback();
}
}
}
void Ticker::attach(Callback func, double delay_sec) {
game_update_callback = func;
game_update_timeout = delay_sec * 60;
}
void Ticker::detach() {
game_update_callback = nullptr;
}
// String helper
std::string DinoGameView::score_to_string(uint32_t score) {
std::string score_s = std::to_string(score);
if (score_s.length() < 5) {
std::string temp = "";
for (uint8_t i = 0; i < 5 - score_s.length(); ++i) temp += "0";
temp += score_s;
score_s = temp;
}
return score_s;
}
// Game timer callback
void game_timer_check() {
if (current_instance) {
if (current_instance->game_state == GameState::PLAYING) {
current_instance->game_loop();
} else if (current_instance->game_state == GameState::MENU) {
current_instance->show_menu();
}
}
}
DinoGameView::DinoGameView(NavigationView& nav)
: nav_{nav}, bird_info{}, game_timer{} {
add_children({&dummy, &button_difficulty});
current_instance = this;
game_timer.attach(&game_timer_check, 1.0 / 60.0);
button_difficulty.on_select = [this](Button&) {
easy_mode = !easy_mode;
button_difficulty.set_text(easy_mode ? "Mode: EASY" : "Mode: HARD");
};
}
void DinoGameView::on_show() {
}
void DinoGameView::paint(Painter& painter) {
(void)painter;
if (!initialized) {
initialized = true;
std::srand(LPC_RTC->CTIME0);
init_game();
}
}
void DinoGameView::frame_sync() {
check_game_timer();
set_dirty();
}
void DinoGameView::init_game() {
game_state = GameState::MENU;
menu_initialized = false;
blink_state = true;
blink_counter = 0;
new_game();
}
void DinoGameView::new_game() {
steps = 0;
score = 0;
collided = false;
ducking = false;
duck_timer = 0;
displayedGameOver = false;
bird_info.inGame = false;
bird_info.x_offset = 320;
bird_info.y_offset = 0;
bird_info.y_velocity = 0;
jumping = false;
falling = false;
jumpHeight = 0;
last_dino_y = DINO_Y;
last_bird_x = -1;
last_bird_y = -1;
last_ducking = false;
last_runstate = false;
ground_offset = 0;
speed_modifier = 0;
runstate = false;
score_drawn = false;
last_score = 999999;
obstacle_spawn_timer = 100; // Initial delay before first obstacle
// Initialize obstacles
for (int i = 0; i < MAX_OBSTACLES; i++) {
obstacles[i].active = false;
obstacles[i].x = -100;
obstacles[i].last_x = -100;
}
cls();
}
void DinoGameView::show_menu() {
if (!menu_initialized) {
cls();
auto style = *ui::Theme::getInstance()->fg_medium;
auto style_title = *ui::Theme::getInstance()->fg_light;
// Draw title
painter.draw_string({80, 60}, style_title, "DINO GAME");
// Draw instructions
painter.draw_string({45, 130}, style, "SELECT: Jump/Start");
painter.draw_string({65, 150}, style, "DOWN: Duck");
painter.draw_string({50, 170}, style, "Avoid obstacles!");
// Draw high score
draw_high_score();
menu_initialized = true;
}
// Show difficulty button
button_difficulty.hidden(false);
// Animate the menu dino
bool menu_run_frame = (blink_counter / 15) % 2;
// Clear previous dino position
fillrect(103, 90, 103 + DINO_WIDTH, 90 + DINO_HEIGHT, Black);
// Draw animated dino
draw_dino_at(103, 90, false, menu_run_frame);
// Blinking start prompt
auto style_prompt = *ui::Theme::getInstance()->fg_light;
if (++blink_counter >= 30) {
blink_counter = 0;
blink_state = !blink_state;
painter.fill_rectangle({55, 258, 130, 20}, Color::black());
if (blink_state) {
painter.draw_string({55, 260}, style_prompt, "* PRESS SELECT *");
}
}
}
void DinoGameView::show_game_over() {
if (!displayedGameOver) {
displayedGameOver = true;
// Clear the last normal dino position
if (last_ducking) {
fillrect(DINO_X, last_dino_y, DINO_X + DINO_DUCK_WIDTH, last_dino_y + DINO_DUCK_HEIGHT, Black);
} else {
fillrect(DINO_X, last_dino_y, DINO_X + DINO_WIDTH, last_dino_y + DINO_HEIGHT, Black);
}
// Draw the game over dino sprite
draw_dino_sprite(DINO_X, DINO_Y, dino_gameover);
auto style = *ui::Theme::getInstance()->fg_light;
auto style_score = *ui::Theme::getInstance()->fg_medium;
// Game over text
painter.draw_string({85, 70}, style, "GAME OVER");
// Show final score
std::string score_text = "SCORE: " + score_to_string(score);
int score_x = (240 - score_text.length() * 8) / 2;
painter.draw_string({score_x, 90}, style_score, score_text);
painter.draw_string({65, 110}, style, "SELECT TO RETRY");
// Update high score
if (score > highScore) {
highScore = score;
painter.draw_string({55, 130}, style, "NEW HIGH SCORE!");
}
}
}
void DinoGameView::game_loop() {
button_difficulty.hidden(true);
if (collided && game_state == GameState::PLAYING) {
game_state = GameState::GAME_OVER;
show_game_over();
return;
}
// Update ground animation
ground_offset = (ground_offset + GAME_SPEED_BASE + speed_modifier) % 20;
// Clear only the game area (not the whole screen to reduce flicker)
draw_ground();
// Update and draw obstacles
update_obstacles();
// Manage bird
manage_bird();
// Handle jumping
if (jumping) {
if (!falling) jumpHeight += JUMP_SPEED + speed_modifier;
if (jumpHeight > JUMP_MAX_HEIGHT && !falling) falling = true;
if (falling) jumpHeight -= JUMP_SPEED + speed_modifier;
if (jumpHeight < 0) {
falling = false;
jumping = false;
jumpHeight = 0;
}
}
// Handle auto-stand from duck
if (ducking && duck_timer > 0) {
duck_timer--;
if (duck_timer == 0) {
stand();
}
}
// Update run animation
if (get_steps() % (10 - speed_modifier) == 0) runstate = !runstate;
// Draw dino with minimal redraw
int current_dino_y = jumping ? (DINO_Y - jumpHeight) : (ducking ? DINO_DUCK_Y : DINO_Y);
// Clear old dino position more precisely
if (current_dino_y != last_dino_y || runstate != last_runstate || ducking != last_ducking) {
// Clear based on last state
if (last_ducking) {
fillrect(DINO_X, last_dino_y, DINO_X + DINO_DUCK_WIDTH, last_dino_y + DINO_DUCK_HEIGHT, Black);
} else {
fillrect(DINO_X, last_dino_y, DINO_X + DINO_WIDTH, last_dino_y + DINO_HEIGHT, Black);
}
}
// Draw dino at new position
if (jumping) {
draw_dino_sprite(DINO_X, current_dino_y, dino_default);
} else if (ducking) {
if (runstate)
draw_dino_sprite(DINO_X, current_dino_y, dino_ducking_leftstep);
else
draw_dino_sprite(DINO_X, current_dino_y, dino_ducking_rightstep);
} else {
if (runstate)
draw_dino_sprite(DINO_X, current_dino_y, dino_leftstep);
else
draw_dino_sprite(DINO_X, current_dino_y, dino_rightstep);
}
// Update last state
last_dino_y = current_dino_y;
last_ducking = ducking;
last_runstate = runstate;
// Check collisions
check_collision();
// Update score
score = get_steps() / 10;
if (score % 100 == 0 && score > 0 && get_steps() % 1000 == 0) {
if (speed_modifier < 5) speed_modifier++;
}
step();
draw_current_score();
draw_high_score();
}
void DinoGameView::draw_ground() {
int ground_y = GAME_AREA_TOP + GAME_AREA_HEIGHT - GROUND_HEIGHT;
// Clear ground area
fillrect(0, ground_y, 320, ground_y + GROUND_HEIGHT, Black);
// Draw ground line
painter.draw_hline({0, ground_y}, 320, Color::white());
painter.draw_hline({0, ground_y + 1}, 320, Color::dark_grey());
// Draw ground texture
for (int x = -ground_offset; x < 320; x += 20) {
painter.draw_hline({x, ground_y + 3}, 10, Color::dark_grey());
painter.draw_hline({x + 5, ground_y + 5}, 5, Color::dark_grey());
}
}
void DinoGameView::update_obstacles() {
// Move obstacles
for (int i = 0; i < MAX_OBSTACLES; i++) {
if (obstacles[i].active) {
// Clear old position
if (obstacles[i].last_x != obstacles[i].x && obstacles[i].last_x < 320) {
clear_obstacle_area(obstacles[i].last_x, obstacles[i].width + 10, obstacles[i].height + 10);
}
obstacles[i].last_x = obstacles[i].x;
obstacles[i].x -= GAME_SPEED_BASE + speed_modifier;
// Remove off-screen obstacles
if (obstacles[i].x < -50) {
obstacles[i].active = false;
clear_obstacle_area(obstacles[i].last_x, obstacles[i].width + 10, obstacles[i].height + 10);
} else {
// Draw obstacle at new position
draw_obstacle(obstacles[i]);
}
}
}
// Decrement spawn timer
if (obstacle_spawn_timer > 0) {
obstacle_spawn_timer -= GAME_SPEED_BASE + speed_modifier;
}
// Check if we should spawn a new obstacle
if (obstacle_spawn_timer <= 0) {
// Try to spawn an obstacle
bool spawned = false;
for (int i = 0; i < MAX_OBSTACLES; i++) {
if (!obstacles[i].active) {
obstacles[i].active = true;
obstacles[i].x = 320;
obstacles[i].last_x = 320;
obstacles[i].type = rand() % 4;
// Set obstacle dimensions based on type
switch (obstacles[i].type) {
case 0: // Small cactus
obstacles[i].width = 15;
obstacles[i].height = 25;
break;
case 1: // Large cactus
obstacles[i].width = 20;
obstacles[i].height = 35;
break;
case 2: // Double cactus
obstacles[i].width = 35;
obstacles[i].height = 30;
break;
case 3: // Triple cactus
obstacles[i].width = 45;
obstacles[i].height = 28;
break;
}
spawned = true;
break;
}
}
if (spawned) {
// Set timer for next obstacle with random variation
obstacle_spawn_timer = MIN_OBSTACLE_DISTANCE + (rand() % (MAX_OBSTACLE_DISTANCE - MIN_OBSTACLE_DISTANCE));
}
}
}
void DinoGameView::clear_obstacle_area(int x, int width, int height) {
int ground_y = GAME_AREA_TOP + GAME_AREA_HEIGHT - GROUND_HEIGHT;
// Only clear the obstacle area, not the entire vertical space
fillrect(x - 5, ground_y - height - 5, x + width + 5, ground_y, Black);
}
void DinoGameView::draw_obstacle(const SimpleObstacle& obstacle) {
if (!obstacle.active) return;
int ground_y = GAME_AREA_TOP + GAME_AREA_HEIGHT - GROUND_HEIGHT;
int y = ground_y - obstacle.height;
// Draw cactus with green color
switch (obstacle.type) {
case 0: // Small single cactus
fillrect(obstacle.x + 5, y, obstacle.x + 10, ground_y, Green);
fillrect(obstacle.x, y + 8, obstacle.x + 5, y + 15, Green);
fillrect(obstacle.x + 10, y + 12, obstacle.x + 15, y + 19, Green);
// Add darker edges for definition
painter.draw_vline({obstacle.x + 4, y + 8}, 7, Color::dark_green());
painter.draw_vline({obstacle.x + 10, y + 12}, 7, Color::dark_green());
break;
case 1: // Large single cactus
fillrect(obstacle.x + 7, y, obstacle.x + 13, ground_y, Green);
fillrect(obstacle.x, y + 10, obstacle.x + 7, y + 20, Green);
fillrect(obstacle.x + 13, y + 15, obstacle.x + 20, y + 25, Green);
fillrect(obstacle.x + 5, y + 5, obstacle.x + 7, y + 12, Green);
fillrect(obstacle.x + 13, y + 8, obstacle.x + 15, y + 15, Green);
// Add darker edges
painter.draw_vline({obstacle.x + 6, y}, ground_y - y, Color::dark_green());
painter.draw_vline({obstacle.x + 13, y}, ground_y - y, Color::dark_green());
break;
case 2: // Double cactus
fillrect(obstacle.x + 5, y + 5, obstacle.x + 10, ground_y, Green);
fillrect(obstacle.x + 20, y, obstacle.x + 25, ground_y, Green);
fillrect(obstacle.x, y + 12, obstacle.x + 5, y + 18, Green);
fillrect(obstacle.x + 10, y + 15, obstacle.x + 15, y + 22, Green);
fillrect(obstacle.x + 25, y + 10, obstacle.x + 30, y + 17, Green);
// Darker outlines
painter.draw_vline({obstacle.x + 4, y + 5}, ground_y - y - 5, Color::dark_green());
painter.draw_vline({obstacle.x + 19, y}, ground_y - y, Color::dark_green());
break;
case 3: // Triple cactus
fillrect(obstacle.x + 5, y + 8, obstacle.x + 10, ground_y, Green);
fillrect(obstacle.x + 20, y, obstacle.x + 25, ground_y, Green);
fillrect(obstacle.x + 35, y + 5, obstacle.x + 40, ground_y, Green);
fillrect(obstacle.x, y + 15, obstacle.x + 5, y + 20, Green);
fillrect(obstacle.x + 25, y + 8, obstacle.x + 30, y + 15, Green);
fillrect(obstacle.x + 40, y + 12, obstacle.x + 45, y + 18, Green);
// Darker outlines
painter.draw_vline({obstacle.x + 4, y + 8}, ground_y - y - 8, Color::dark_green());
painter.draw_vline({obstacle.x + 19, y}, ground_y - y, Color::dark_green());
painter.draw_vline({obstacle.x + 34, y + 5}, ground_y - y - 5, Color::dark_green());
break;
}
}
void DinoGameView::manage_bird() {
if (!bird_info.inGame) {
// Only spawn bird if no obstacles are too close
bool obstacle_nearby = false;
for (int i = 0; i < MAX_OBSTACLES; i++) {
if (obstacles[i].active && obstacles[i].x > 200) {
obstacle_nearby = true;
break;
}
}
// Randomly spawn bird if no nearby obstacles
if (!obstacle_nearby && rand() % 400 == 0 && get_steps() > 100) {
bird_info.inGame = true;
bird_info.x_offset = 320;
bird_info.y_offset = 0;
bird_info.y_velocity = easy_mode ? 0 : (rand() % 3) - 1; // No vertical movement in easy mode
bird_info.y_position = BirdPosition::DOWN; // Always spawn at standing height in easy mode
}
} else {
// Calculate bird Y position
int base_y = (bird_info.y_position == BirdPosition::UP) ? BIRD_Y_UP : BIRD_Y_DOWN;
int current_y = base_y + bird_info.y_offset;
// Clear previous bird position
if (last_bird_x < 320 && last_bird_x >= -BIRD_WIDTH) {
int clear_start_x = last_bird_x;
int clear_end_x = last_bird_x + BIRD_WIDTH;
if (clear_start_x < 0) clear_start_x = 0;
if (clear_end_x > 320) clear_end_x = 320;
if (clear_end_x > clear_start_x) {
fillrect(clear_start_x, last_bird_y, clear_end_x, last_bird_y + BIRD_HEIGHT, Black);
}
}
// Move bird
bird_info.x_offset -= GAME_SPEED_BASE + speed_modifier + 1;
// Update vertical position only in hard mode
if (!easy_mode) {
bird_info.y_offset += bird_info.y_velocity;
if (rand() % 30 == 0) {
bird_info.y_velocity = (rand() % 3) - 1;
}
// Keep bird within bounds
if (current_y < GAME_AREA_TOP + 10) {
current_y = GAME_AREA_TOP + 10;
bird_info.y_offset = current_y - base_y;
bird_info.y_velocity = 1;
} else if (current_y > GAME_AREA_TOP + GAME_AREA_HEIGHT - GROUND_HEIGHT - BIRD_HEIGHT - 10) {
current_y = GAME_AREA_TOP + GAME_AREA_HEIGHT - GROUND_HEIGHT - BIRD_HEIGHT - 10;
bird_info.y_offset = current_y - base_y;
bird_info.y_velocity = -1;
}
} else {
// In easy mode, keep bird at perfect ducking height
// Bird should hit standing dino but miss ducking dino
current_y = DINO_Y - 10; // Position bird just above ducking height
}
// Animate wings
if (get_steps() % 15 == 0) bird_info.flop = !bird_info.flop;
// Draw bird only if any part is visible
if (bird_info.x_offset > -BIRD_WIDTH && bird_info.x_offset < 320) {
if (bird_info.flop) {
draw_bird_sprite(bird_info.x_offset, current_y, pterodactyl_upflop);
} else {
draw_bird_sprite(bird_info.x_offset, current_y, pterodactyl_downflop);
}
}
// Update last position
last_bird_x = bird_info.x_offset;
last_bird_y = current_y;
// Remove bird only when completely off screen
if (bird_info.x_offset < -BIRD_WIDTH - 5) {
bird_info.inGame = false;
last_bird_x = -1;
last_bird_y = -1;
}
}
}
void DinoGameView::draw_dino_sprite(int x, int y, const uint16_t* sprite) {
int height, width;
// Determine dimensions based on which sprite we're drawing
if (sprite == dino_ducking_leftstep || sprite == dino_ducking_rightstep) {
height = DINO_DUCK_HEIGHT;
width = DINO_DUCK_WIDTH;
} else {
height = DINO_HEIGHT;
width = DINO_WIDTH;
}
for (int dy = 0; dy < height; dy++) {
int run_start = -1;
uint16_t run_color = 0;
for (int dx = 0; dx < width; dx++) {
uint16_t pixel = sprite[dy * width + dx];
if (pixel != TRANSPARENT_COLOR) {
if (run_start == -1 || pixel != run_color) {
// Draw previous run if any
if (run_start != -1) {
painter.fill_rectangle({x + run_start, y + dy, dx - run_start, 1}, Color(run_color));
}
run_start = dx;
run_color = pixel;
}
} else {
// Draw previous run if any
if (run_start != -1) {
painter.fill_rectangle({x + run_start, y + dy, dx - run_start, 1}, Color(run_color));
run_start = -1;
}
}
}
// Draw final run if any
if (run_start != -1) {
painter.fill_rectangle({x + run_start, y + dy, width - run_start, 1}, Color(run_color));
}
}
}
void DinoGameView::draw_bird_sprite(int x, int y, const uint16_t* sprite) {
for (int dy = 0; dy < BIRD_HEIGHT; dy++) {
int run_start = -1;
uint16_t run_color = 0;
for (int dx = 0; dx < BIRD_WIDTH; dx++) {
// Skip pixels that would be off-screen
if (x + dx < 0 || x + dx >= 320) {
// End any current run
if (run_start != -1) {
painter.fill_rectangle({x + run_start, y + dy, dx - run_start, 1}, Color(run_color));
run_start = -1;
}
continue;
}
uint16_t pixel = sprite[dy * BIRD_WIDTH + dx];
if (pixel != TRANSPARENT_COLOR) {
if (pixel == SPRITE_COLOR) {
pixel = Color::red().v;
}
if (run_start == -1 || pixel != run_color) {
if (run_start != -1) {
painter.fill_rectangle({x + run_start, y + dy, dx - run_start, 1}, Color(run_color));
}
run_start = dx;
run_color = pixel;
}
} else {
if (run_start != -1) {
painter.fill_rectangle({x + run_start, y + dy, dx - run_start, 1}, Color(run_color));
run_start = -1;
}
}
}
if (run_start != -1 && x + run_start < 320) {
int width = BIRD_WIDTH - run_start;
if (x + run_start + width > 320) {
width = 320 - (x + run_start);
}
if (width > 0) {
painter.fill_rectangle({x + run_start, y + dy, width, 1}, Color(run_color));
}
}
}
}
void DinoGameView::draw_dino_at(int x, int y, bool is_ducking, bool run_frame) {
if (is_ducking) {
if (run_frame) {
draw_dino_sprite(x, y, dino_ducking_leftstep);
} else {
draw_dino_sprite(x, y, dino_ducking_rightstep);
}
} else {
if (run_frame) {
draw_dino_sprite(x, y, dino_leftstep);
} else {
draw_dino_sprite(x, y, dino_default);
}
}
}
void DinoGameView::check_collision() {
int dino_left = DINO_X;
int dino_right = DINO_X + (ducking ? DINO_DUCK_WIDTH : DINO_WIDTH);
int dino_top = ducking ? DINO_DUCK_Y : (DINO_Y - jumpHeight);
int dino_bottom = dino_top + (ducking ? DINO_DUCK_HEIGHT : DINO_HEIGHT);
// Give a small hitbox reduction for fairness
dino_left += 5;
dino_right -= 5;
dino_top += 5;
dino_bottom -= 5;
// Check cactus collisions
for (int i = 0; i < MAX_OBSTACLES; i++) {
if (obstacles[i].active) {
int ground_y = GAME_AREA_TOP + GAME_AREA_HEIGHT - GROUND_HEIGHT;
int obs_top = ground_y - obstacles[i].height;
int obs_bottom = ground_y;
if (dino_right > obstacles[i].x &&
dino_left < obstacles[i].x + obstacles[i].width &&
dino_bottom > obs_top &&
dino_top < obs_bottom) {
collided = true;
return;
}
}
}
// Check bird collision
if (bird_info.inGame) {
int base_y = (bird_info.y_position == BirdPosition::UP) ? BIRD_Y_UP : BIRD_Y_DOWN;
int bird_y = base_y + bird_info.y_offset;
if (dino_right > bird_info.x_offset + 5 &&
dino_left < bird_info.x_offset + BIRD_WIDTH - 5 &&
dino_bottom > bird_y + 5 &&
dino_top < bird_y + BIRD_HEIGHT - 5) {
collided = true;
}
}
}
void DinoGameView::draw_current_score() {
auto style = *ui::Theme::getInstance()->fg_medium;
if (last_score != score) {
painter.fill_rectangle({10, 28, 60, 14}, Color::black());
painter.draw_string({10, 30}, style, score_to_string(score));
last_score = score;
}
}
void DinoGameView::draw_high_score() {
auto style = *ui::Theme::getInstance()->fg_light;
painter.fill_rectangle({152, 28, 100, 14}, Color::black());
painter.draw_string({152, 30}, style, "HI " + score_to_string(highScore));
}
void DinoGameView::jump() {
if (!jumping) {
jumping = true;
// If we're ducking when we jump, stand up
if (ducking) {
stand();
}
}
}
void DinoGameView::duck() {
if (!jumping) {
ducking = true;
duck_timer = 60; // 1 second at 60 FPS
}
}
void DinoGameView::stand() {
ducking = false;
duck_timer = 0;
}
void DinoGameView::step() {
++steps;
}
bool DinoGameView::on_key(const KeyEvent key) {
if (key == KeyEvent::Select) {
if (game_state == GameState::MENU) {
game_state = GameState::PLAYING;
new_game();
draw_high_score();
} else if (game_state == GameState::PLAYING && !collided) {
jump();
} else if (game_state == GameState::GAME_OVER || collided) {
game_state = GameState::PLAYING; // Restart immediately
new_game();
draw_high_score();
}
} else if (key == KeyEvent::Down) {
if (game_state == GameState::PLAYING && !collided) {
duck();
}
} else if (key == KeyEvent::Up) {
if (game_state == GameState::PLAYING && !collided) {
stand();
}
}
set_dirty();
return true;
}
bool DinoGameView::on_encoder(const EncoderEvent delta) {
(void)delta;
return true;
}
} // namespace ui::external_app::dinogame
+254
View File
@@ -0,0 +1,254 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
/*
* Chrome Dino Game for Portapack Mayhem
* Based on the original DinoGame by various contributors
*/
#ifndef __UI_DINOGAME_H__
#define __UI_DINOGAME_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include "irq_controls.hpp"
#include "random.hpp"
#include "lpc43xx_cpp.hpp"
#include "ui_widget.hpp"
#include "file.hpp"
#include <string>
#include "app_settings.hpp"
namespace ui::external_app::dinogame {
// Color definitions
enum {
White,
Blue,
Yellow,
Purple,
Green,
Red,
Maroon,
Orange,
Black,
};
// Game constants
#define DINO_WIDTH 34
#define DINO_HEIGHT 36
#define DINO_DUCK_WIDTH 45
#define DINO_DUCK_HEIGHT 22
#define BIRD_WIDTH 34
#define BIRD_HEIGHT 27
#define GROUND_HEIGHT 10
#define GAME_AREA_TOP 78
#define GAME_AREA_HEIGHT 160
#define DINO_X 30
#define DINO_Y (GAME_AREA_TOP + GAME_AREA_HEIGHT - GROUND_HEIGHT - DINO_HEIGHT)
#define DINO_DUCK_Y (GAME_AREA_TOP + GAME_AREA_HEIGHT - GROUND_HEIGHT - DINO_DUCK_HEIGHT)
#define BIRD_Y_UP (GAME_AREA_TOP + 20)
#define BIRD_Y_DOWN (GAME_AREA_TOP + 60)
#define JUMP_MAX_HEIGHT 70
#define JUMP_SPEED 3
#define GAME_SPEED_BASE 3
#define SPRITE_COLOR 0x528A
#define TRANSPARENT_COLOR 0xFFFF
#define MIN_OBSTACLE_DISTANCE 300
#define MAX_OBSTACLE_DISTANCE 600
// Game states
enum class GameState {
MENU,
PLAYING,
GAME_OVER
};
// Walk styles
enum class WalkStyle {
WALKING,
DUCKING
};
// Bird position
enum class BirdPosition {
UP,
DOWN
};
// Structures
struct Pterodactyl {
bool inGame = false;
BirdPosition y_position = BirdPosition::UP;
int16_t x_offset = 320;
int16_t y_offset = 0;
int8_t y_velocity = 0;
bool flop = false;
};
// Simple obstacle structure with proper initialization
struct SimpleObstacle {
bool active;
int16_t x;
int16_t last_x;
uint8_t width;
uint8_t height;
uint8_t type; // 0=small, 1=large, 2=double, 3=triple
SimpleObstacle()
: active(false), x(0), last_x(0), width(0), height(0), type(0) {}
};
// External references to sprite data
extern const uint16_t dino_default[];
extern const uint16_t dino_leftstep[];
extern const uint16_t dino_rightstep[];
extern const uint16_t dino_ducking_leftstep[];
extern const uint16_t dino_ducking_rightstep[];
extern const uint16_t dino_gameover[];
extern const uint16_t pterodactyl_upflop[];
extern const uint16_t pterodactyl_downflop[];
// Global painter
extern Painter painter;
// Function declarations
void cls();
void fillrect(int x1, int y1, int x2, int y2, int color);
void rect(int x1, int y1, int x2, int y2, int color);
void check_game_timer();
void game_timer_check();
// Ticker class
using Callback = void (*)(void);
class Ticker {
public:
Ticker() = default;
void attach(Callback func, double delay_sec);
void detach();
};
// Main view class
class DinoGameView : public View {
public:
DinoGameView(NavigationView& nav);
void on_show() override;
std::string title() const override { return "Dino Game"; }
void focus() override { dummy.focus(); }
void paint(Painter& painter) override;
void frame_sync();
bool on_encoder(const EncoderEvent event) override;
bool on_key(KeyEvent key) override;
// Public for timer callback
GameState game_state = GameState::MENU;
void game_loop();
void show_menu();
private:
bool initialized = false;
NavigationView& nav_;
// Game variables
static constexpr uint8_t MAX_OBSTACLES = 1;
SimpleObstacle obstacles[MAX_OBSTACLES];
Pterodactyl bird_info;
int16_t jumpHeight = 0;
uint8_t ground_offset = 0;
uint8_t speed_modifier = 0;
bool runstate = false;
bool jumping = false;
bool falling = false;
bool collided = false;
bool ducking = false;
bool displayedGameOver = false;
bool last_ducking = false;
bool last_runstate = false;
uint32_t steps = 0;
uint32_t score = 0;
uint32_t highScore = 0;
uint32_t last_score = 999999; // Initialize to impossible value
int32_t next_obstacle_distance = 100;
int16_t last_obstacle_x = 320; // Track position of last spawned obstacle
int32_t obstacle_spawn_timer = 0;
// Position tracking for minimal redraw
int16_t last_dino_y = DINO_Y;
int16_t last_bird_x = -1;
int16_t last_bird_y = -1;
uint8_t duck_timer = 0;
// Menu animation
bool menu_initialized = false;
bool blink_state = true;
uint32_t blink_counter = 0;
// Current Score
bool score_drawn = false;
// Game timer
Ticker game_timer;
// Private methods
void init_game();
void new_game();
void update_obstacles();
void spawn_obstacle();
void draw_screen();
void draw_ground();
void draw_obstacle(const SimpleObstacle& obstacle);
void clear_obstacle_area(int x, int width, int height);
void draw_dino_sprite(int x, int y, const uint16_t* sprite);
void draw_bird_sprite(int x, int y, const uint16_t* sprite);
void draw_dino_at(int x, int y, bool is_ducking, bool run_frame);
void manage_bird();
void draw_score();
void draw_high_score();
void draw_current_score();
void show_game_over();
void jump();
void duck();
void stand();
void check_collision();
void step();
uint32_t get_steps() { return steps; }
std::string score_to_string(uint32_t score);
bool easy_mode = false;
Button button_difficulty{
{70, 195, 100, 20},
"Mode: HARD"};
app_settings::SettingsManager settings_{
"dinogame",
app_settings::Mode::NO_RF,
{{"highscore"sv, &highScore},
{"easy_mode"sv, &easy_mode}}};
Button dummy{
{screen_width, 0, 0, 0},
""};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->frame_sync();
}};
};
} // namespace ui::external_app::dinogame
#endif /* __UI_DINOGAME_H__ */
+5
View File
@@ -193,6 +193,10 @@ set(EXTCPPSRC
external/breakout/main.cpp
external/breakout/ui_breakout.cpp
#dinogame
external/dinogame/main.cpp
external/dinogame/ui_dinogame.cpp
#doom
external/doom/main.cpp
external/doom/ui_doom.cpp
@@ -265,6 +269,7 @@ set(EXTAPPLIST
snake
stopwatch
breakout
dinogame
doom
debug_pmem
scanner
+6
View File
@@ -75,6 +75,7 @@ MEMORY
ram_external_app_gfxeq (rwx) : org = 0xADE20000, len = 32k
ram_external_app_noaaapt_rx (rwx) : org = 0xADE30000, len = 32k
ram_external_app_detector_rx (rwx) : org = 0xADE40000, len = 32k
ram_external_app_dinogame (rwx) : org = 0xADE50000, len = 32k
}
SECTIONS
@@ -391,6 +392,11 @@ SECTIONS
*(*ui*external_app*detector_rx*);
} > ram_external_app_detector_rx
.external_app_dinogame : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_dinogame.application_information));
*(*ui*external_app*dinogame*);
} > ram_external_app_dinogame
}
+1 -1
View File
@@ -38,7 +38,7 @@ __attribute__((section(".external_app.app_fmradio.application_information"), use
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "FM Radio",
/*.app_name = */ "Radio",
/*.bitmap_data = */ {
0x00,
0x00,
+48 -7
View File
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2024 HT Otto
* Copyright (C) 2025 RocketGod - Added modes from my Flipper Zero RF Jammer App - https://betaskynet.com
* Copyright (C) 2024 HTotoo
* Copyright (C) 2025 RocketGod
*
* This file is part of PortaPack.
*
@@ -35,10 +35,34 @@ using namespace ui;
namespace ui::external_app::fmradio {
#include "external/ui_grapheq.cpi"
void FmRadioView::focus() {
field_frequency.focus();
}
void FmRadioView::show_hide_gfx(bool show) {
gr.hidden(!show);
gr.set_paused(!show);
waveform.set_paused(show);
btn_fav_0.hidden(show);
btn_fav_1.hidden(show);
btn_fav_2.hidden(show);
btn_fav_3.hidden(show);
btn_fav_4.hidden(show);
btn_fav_5.hidden(show);
btn_fav_6.hidden(show);
btn_fav_7.hidden(show);
btn_fav_8.hidden(show);
btn_fav_9.hidden(show);
txt_save_help.hidden(show);
btn_fav_save.hidden(show);
field_bw.hidden(show);
field_modulation.hidden(show);
text_mode_label.hidden(show);
set_dirty();
}
void FmRadioView::change_mode(int32_t mod) {
field_bw.on_change = [this](size_t n, OptionsField::value_t) { (void)n; };
@@ -48,8 +72,8 @@ void FmRadioView::change_mode(int32_t mod) {
audio_spectrum_update = false; // Reset spectrum update flag
std::fill(audio_spectrum, audio_spectrum + 128, 0); // Clear spectrum buffer
ReceiverModel::Mode receiver_mode = static_cast<ReceiverModel::Mode>(mod);
waveform.set_dirty();
receiver_mode = static_cast<ReceiverModel::Mode>(mod);
bool is_ssb = (mod == static_cast<int32_t>(ReceiverModel::Mode::AMAudio) &&
(field_modulation.selected_index() == 3 || field_modulation.selected_index() == 4));
@@ -70,6 +94,7 @@ void FmRadioView::change_mode(int32_t mod) {
radio_bw = index;
receiver_model.set_am_configuration(n);
};
show_hide_gfx(false);
break;
case static_cast<int32_t>(ReceiverModel::Mode::NarrowbandFMAudio):
audio_sampling_rate = audio::Rate::Hz_24000;
@@ -82,6 +107,7 @@ void FmRadioView::change_mode(int32_t mod) {
radio_bw = index;
receiver_model.set_nbfm_configuration(n);
};
show_hide_gfx(false);
break;
case static_cast<int32_t>(ReceiverModel::Mode::WidebandFMAudio):
audio_sampling_rate = audio::Rate::Hz_48000;
@@ -106,7 +132,6 @@ void FmRadioView::change_mode(int32_t mod) {
audio::set_rate(audio_sampling_rate);
audio::output::start();
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack
receiver_model.enable();
}
@@ -136,8 +161,10 @@ FmRadioView::FmRadioView(NavigationView& nav)
&btn_fav_9,
&audio,
&waveform,
&rssi});
&rssi,
&gr});
txt_save_help.set_focusable(false);
txt_save_help.visible(false);
for (uint8_t i = 0; i < 12; ++i) {
if (freq_fav_list[i].frequency == 0) {
@@ -179,7 +206,20 @@ FmRadioView::FmRadioView(NavigationView& nav)
}
};
waveform.on_select = [this](Waveform&) {
if (receiver_mode != ReceiverModel::Mode::WidebandFMAudio) { // only there is spectrum message
return;
}
show_hide_gfx(!btn_fav_0.hidden());
};
gr.set_theme(themes[current_theme].base_color, themes[current_theme].peak_color);
gr.on_select = [this](GraphEq&) {
current_theme = (current_theme + 1) % themes.size();
gr.set_theme(themes[current_theme].base_color, themes[current_theme].peak_color);
gr.set_paused(false);
};
update_fav_btn_texts();
show_hide_gfx(false);
}
void FmRadioView::on_btn_clicked(uint8_t i) {
@@ -226,8 +266,9 @@ FmRadioView::~FmRadioView() {
}
void FmRadioView::on_audio_spectrum() {
if (gr.visible() && audio_spectrum_data) gr.update_audio_spectrum(*audio_spectrum_data);
if (audio_spectrum_data && audio_spectrum_data->db.size() <= 128) {
for (size_t i = 0; i < audio_spectrum_data->db.size(); i++) {
for (size_t i = 0; i < audio_spectrum_data->db.size(); ++i) {
audio_spectrum[i] = ((int16_t)audio_spectrum_data->db[i] - 127) * 256;
}
waveform.set_dirty();
+40 -5
View File
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2024 HTotoo
* Copyright (C) 2025 RocketGod - Added modes from my Flipper Zero RF Jammer App - https://betaskynet.com
* Copyright (C) 2025 RocketGod
*
* This file is part of PortaPack.
*
@@ -47,6 +47,8 @@ using namespace ui;
namespace ui::external_app::fmradio {
#include "external/ui_grapheq.hpp"
#define FMR_BTNGRID_TOP 60
class FmRadioView : public View {
@@ -58,13 +60,14 @@ class FmRadioView : public View {
void focus() override;
std::string title() const override { return "FM radio"; };
std::string title() const override { return "Radio"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{};
int16_t audio_spectrum[128]{0};
bool audio_spectrum_update = false;
ReceiverModel::Mode receiver_mode = ReceiverModel::Mode::WidebandFMAudio;
AudioSpectrum* audio_spectrum_data{nullptr};
struct Favorite {
rf::Frequency frequency = 0;
@@ -74,7 +77,7 @@ class FmRadioView : public View {
Favorite freq_fav_list[12];
audio::Rate audio_sampling_rate = audio::Rate::Hz_48000;
uint8_t radio_bw = 0;
uint32_t current_theme{0};
app_settings::SettingsManager settings_{
"rx_fmradio",
app_settings::Mode::RX,
@@ -114,7 +117,8 @@ class FmRadioView : public View {
{"favlist9_bw"sv, &freq_fav_list[9].bandwidth},
{"favlist10_bw"sv, &freq_fav_list[10].bandwidth},
{"favlist11_bw"sv, &freq_fav_list[11].bandwidth},
{"radio_bw"sv, &radio_bw}}};
{"radio_bw"sv, &radio_bw},
{"theme"sv, &current_theme}}};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
@@ -157,7 +161,7 @@ class FmRadioView : public View {
{21 * 8, 10, 6 * 8, 4}};
Waveform waveform{
{0, 20, screen_width, 2 * 16},
{0, 20, UI_POS_MAXWIDTH, 2 * 16},
audio_spectrum,
128,
0,
@@ -165,6 +169,8 @@ class FmRadioView : public View {
Theme::getInstance()->bg_darkest->foreground,
true};
GraphEq gr{{2, FMR_BTNGRID_TOP, UI_POS_MAXWIDTH - 4, UI_POS_MAXHEIGHT - FMR_BTNGRID_TOP}, true};
Button btn_fav_0{{2, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_1{{2 + 15 * 8, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_2{{2, FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"};
@@ -185,6 +191,35 @@ class FmRadioView : public View {
void on_audio_spectrum();
void change_mode(int32_t mod);
void show_hide_gfx(bool show);
struct ColorTheme {
Color base_color;
Color peak_color;
};
const std::array<ColorTheme, 20> themes{
ColorTheme{Color(255, 0, 255), Color(255, 255, 255)},
ColorTheme{Color(0, 255, 0), Color(255, 0, 0)},
ColorTheme{Color(0, 0, 255), Color(255, 255, 0)},
ColorTheme{Color(255, 128, 0), Color(255, 0, 128)},
ColorTheme{Color(128, 0, 255), Color(0, 255, 255)},
ColorTheme{Color(255, 255, 0), Color(0, 255, 128)},
ColorTheme{Color(255, 0, 0), Color(0, 128, 255)},
ColorTheme{Color(0, 255, 128), Color(255, 128, 255)},
ColorTheme{Color(128, 128, 128), Color(255, 255, 255)},
ColorTheme{Color(255, 64, 0), Color(0, 255, 64)},
ColorTheme{Color(0, 128, 128), Color(255, 192, 0)},
ColorTheme{Color(0, 255, 0), Color(0, 128, 0)},
ColorTheme{Color(32, 64, 32), Color(0, 255, 0)},
ColorTheme{Color(64, 0, 128), Color(255, 0, 255)},
ColorTheme{Color(0, 64, 0), Color(0, 255, 128)},
ColorTheme{Color(255, 255, 255), Color(0, 0, 255)},
ColorTheme{Color(128, 0, 0), Color(255, 128, 0)},
ColorTheme{Color(0, 128, 255), Color(255, 255, 128)},
ColorTheme{Color(64, 64, 64), Color(255, 0, 0)},
ColorTheme{Color(255, 192, 0), Color(0, 64, 128)}};
MessageHandlerRegistration message_handler_audio_spectrum{
Message::ID::AudioSpectrum,
[this](const Message* const p) {
+6 -104
View File
@@ -20,10 +20,12 @@ using namespace portapack;
namespace ui::external_app::gfxeq {
#include "external/ui_grapheq.cpi"
gfxEQView::gfxEQView(NavigationView& nav)
: nav_{nav}, bar_heights(NUM_BARS, 0), prev_bar_heights(NUM_BARS, 0) {
: nav_{nav} {
add_children({&button_frequency, &field_rf_amp, &field_lna, &field_vga,
&button_mood, &field_volume});
&button_mood, &field_volume, &gr});
audio::output::stop();
receiver_model.disable();
@@ -69,6 +71,7 @@ gfxEQView::gfxEQView(NavigationView& nav)
};
button_mood.on_select = [this](Button&) { this->cycle_theme(); };
gr.set_theme(themes[current_theme].base_color, themes[current_theme].peak_color);
}
// needed to answer usb serial frequency set
@@ -87,110 +90,9 @@ void gfxEQView::focus() {
button_frequency.focus();
}
void gfxEQView::on_show() {
needs_background_redraw = true;
}
void gfxEQView::on_hide() {
needs_background_redraw = true;
}
void gfxEQView::update_audio_spectrum(const AudioSpectrum& spectrum) {
const float bin_frequency_size = 48000.0f / 128;
for (int bar = 0; bar < NUM_BARS; bar++) {
float start_freq = FREQUENCY_BANDS[bar];
float end_freq = FREQUENCY_BANDS[bar + 1];
int start_bin = std::max(1, (int)(start_freq / bin_frequency_size));
int end_bin = std::min(127, (int)(end_freq / bin_frequency_size));
if (start_bin >= end_bin) {
end_bin = start_bin + 1;
}
float total_energy = 0;
int bin_count = 0;
for (int bin = start_bin; bin <= end_bin; bin++) {
total_energy += spectrum.db[bin];
bin_count++;
}
float avg_db = bin_count > 0 ? (total_energy / bin_count) : 0;
// Manually boost highs for better visual balance
float treble_boost = 1.0f;
if (bar == 10)
treble_boost = 1.7f;
else if (bar >= 9)
treble_boost = 1.3f;
else if (bar >= 7)
treble_boost = 1.3f;
// Mid emphasis for a V-shape effect
float mid_boost = 1.0f;
if (bar == 4 || bar == 5 || bar == 6) mid_boost = 1.2f;
float amplified_db = avg_db * treble_boost * mid_boost;
if (amplified_db > 255) amplified_db = 255;
float band_scale = 1.0f;
int target_height = (amplified_db * RENDER_HEIGHT * band_scale) / 255;
if (target_height > RENDER_HEIGHT) {
target_height = RENDER_HEIGHT;
}
// Adjusted to look nice to my eyes
float rise_speed = 0.8f;
float fall_speed = 1.0f;
if (target_height > bar_heights[bar]) {
bar_heights[bar] = bar_heights[bar] * (1.0f - rise_speed) + target_height * rise_speed;
} else {
bar_heights[bar] = bar_heights[bar] * (1.0f - fall_speed) + target_height * fall_speed;
}
}
}
void gfxEQView::render_equalizer(Painter& painter) {
const int num_segments = RENDER_HEIGHT / SEGMENT_HEIGHT;
const ColorTheme& theme = themes[current_theme];
for (int bar = 0; bar < NUM_BARS; bar++) {
int x = HORIZONTAL_OFFSET + bar * (BAR_WIDTH + BAR_SPACING);
int active_segments = (bar_heights[bar] * num_segments) / RENDER_HEIGHT;
if (prev_bar_heights[bar] > active_segments) {
int clear_height = (prev_bar_heights[bar] - active_segments) * SEGMENT_HEIGHT;
int clear_y = screen_height - prev_bar_heights[bar] * SEGMENT_HEIGHT;
painter.fill_rectangle({x, clear_y, BAR_WIDTH, clear_height}, Color(0, 0, 0));
}
for (int seg = 0; seg < active_segments; seg++) {
int y = screen_height - (seg + 1) * SEGMENT_HEIGHT;
if (y < header_height) break;
Color segment_color = (seg >= active_segments - 2 && seg < active_segments) ? theme.peak_color : theme.base_color;
painter.fill_rectangle({x, y, BAR_WIDTH, SEGMENT_HEIGHT - 1}, segment_color);
}
prev_bar_heights[bar] = active_segments;
}
}
void gfxEQView::paint(Painter& painter) {
if (needs_background_redraw) {
painter.fill_rectangle({0, header_height, screen_width, RENDER_HEIGHT}, Color(0, 0, 0));
needs_background_redraw = false;
}
render_equalizer(painter);
}
void gfxEQView::cycle_theme() {
current_theme = (current_theme + 1) % themes.size();
gr.set_theme(themes[current_theme].base_color, themes[current_theme].peak_color);
}
} // namespace ui::external_app::gfxeq
+5 -33
View File
@@ -22,6 +22,8 @@
namespace ui::external_app::gfxeq {
#include "external/ui_grapheq.hpp"
class gfxEQView : public View {
public:
gfxEQView(NavigationView& nav);
@@ -32,45 +34,17 @@ class gfxEQView : public View {
void focus() override;
std::string title() const override { return "gfxEQ"; }
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
void on_freqchg(int64_t freq);
private:
static constexpr ui::Dim header_height = 2 * 16;
static constexpr int RENDER_HEIGHT = 288;
static constexpr int NUM_BARS = 11;
static constexpr int BAR_SPACING = 2;
int BAR_WIDTH = (screen_width - (BAR_SPACING * (NUM_BARS - 1))) / NUM_BARS;
static constexpr int HORIZONTAL_OFFSET = 2;
static constexpr int SEGMENT_HEIGHT = 10;
static constexpr std::array<int, NUM_BARS + 1> FREQUENCY_BANDS = {
375, // Bass warmth and low rumble (e.g., deep basslines, kick drum body)
750, // Upper bass punch (e.g., bass guitar punch, kick drum attack)
1500, // Lower midrange fullness (e.g., warmth in vocals, guitar body)
2250, // Midrange clarity (e.g., vocal presence, snare crack)
3375, // Upper midrange bite (e.g., instrument definition, vocal articulation)
4875, // Presence and edge (e.g., guitar bite, vocal sibilance start)
6750, // Lower brilliance (e.g., cymbal shimmer, vocal clarity)
9375, // Brilliance and air (e.g., hi-hat crispness, breathy vocals)
13125, // High treble sparkle (e.g., subtle overtones, synth shimmer)
16875, // Upper treble airiness (e.g., faint harmonics, room ambiance)
20625, // Top-end sheen (e.g., ultra-high harmonics, noise floor)
24375 // Extreme treble limit (e.g., inaudible overtones, signal cutoff, static)
};
struct ColorTheme {
Color base_color;
Color peak_color;
};
NavigationView& nav_;
bool needs_background_redraw{false};
std::vector<int> bar_heights;
std::vector<int> prev_bar_heights;
uint32_t current_theme{0};
const std::array<ColorTheme, 20> themes{
ColorTheme{Color(255, 0, 255), Color(255, 255, 255)},
@@ -100,6 +74,7 @@ class gfxEQView : public View {
VGAGainField field_vga{{18 * 8, 0 * 16}};
Button button_mood{{21 * 8, 0, 6 * 8, 16}, "MOOD"};
AudioVolumeField field_volume{{screen_width - 2 * 8, 0 * 16}};
GraphEq gr{{2, UI_POS_DEFAULT_HEIGHT, UI_POS_MAXWIDTH - 4, UI_POS_HEIGHT_REMAINING(2)}, false};
rf::Frequency frequency_value{93100000};
@@ -111,16 +86,13 @@ class gfxEQView : public View {
{{"theme", &current_theme},
{"frequency", &frequency_value}}};
void update_audio_spectrum(const AudioSpectrum& spectrum);
void render_equalizer(Painter& painter);
void cycle_theme();
MessageHandlerRegistration message_handler_audio_spectrum{
Message::ID::AudioSpectrum,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
this->update_audio_spectrum(*message.data);
this->set_dirty();
this->gr.update_audio_spectrum(*message.data);
}};
MessageHandlerRegistration message_handler_freqchg{
+20 -5
View File
@@ -78,7 +78,7 @@ void HopperView::set_hopper_channel(uint32_t i, uint32_t width, uint64_t center,
hopper_channels[i].enabled = true;
hopper_channels[i].width = (width * 0xFFFFFFULL) / 1536000;
hopper_channels[i].center = center;
hopper_channels[i].duration = 30720 * duration;
hopper_channels[i].duration = duration ? 3072 * duration : 10;
}
void HopperView::start_tx() {
@@ -274,7 +274,7 @@ HopperView::HopperView(
options_type.set_selected_index(3); // Rand CW
options_speed.set_selected_index(3); // 10kHz
options_hop.set_selected_index(1); // 50ms
options_hop.set_selected_index(3); // 50ms
button_transmit.set_style(&style_val);
field_timetx.set_value(30);
@@ -327,10 +327,25 @@ HopperView::HopperView(
};
button_transmit.on_select = [this](Button&) {
if (jamming || cooling)
if (jamming || cooling) {
stop_tx();
else
start_tx();
} else {
// if hop speed is 0, alert the user that this will cause a freeze on UI
if (options_hop.selected_index_value() == 0) {
nav_.display_modal(
"Warning", "Hopping set to 0ms (fastest).\n\nTHIS WILL FREEZE THE HACKRF,\npress RESET button to stop\n\nAre you sure?", YESNO, [this](bool choice) {
if (choice) {
// Wait for UI update before the freeze
chThdSleepMilliseconds(50);
start_tx();
}
},
TRUE);
} else {
// if hop speed is not 0, just start the transmission
start_tx();
}
}
};
menu_freq_list.on_left = [this]() {
+17 -8
View File
@@ -143,6 +143,13 @@ class HopperView : public View {
{"FM tone", 1},
{"CW sweep", 2},
{"Rand CW", 3},
{"Sine", 4},
{"Square", 5},
{"Sawtooth", 6},
{"Triangle", 7},
{"Chirp", 8},
{"Gauss", 9},
{"Brute", 10},
}};
Text text_range_number{
@@ -163,14 +170,16 @@ class HopperView : public View {
OptionsField options_hop{
{7 * 8, 27 * 8},
5,
{{"10ms ", 1},
{"50ms ", 5},
{"100ms", 10},
{"1s ", 100},
{"2s ", 200},
{"5s ", 500},
{"10s ", 1000}}};
6,
{{"0ms !!", 0},
{"1ms ", 1},
{"10ms ", 10},
{"50ms ", 50},
{"100ms", 100},
{"1s ", 1000},
{"2s ", 2000},
{"5s ", 5000},
{"10s ", 10000}}};
NumberField field_timetx{
{7 * 8, 29 * 8},
+1
View File
@@ -52,3 +52,4 @@ const std::filesystem::path ook_editor_dir = u"OOKFILES";
const std::filesystem::path hopper_dir = u"HOPPER";
const std::filesystem::path subghz_dir = u"SUBGHZ";
const std::filesystem::path waterfalls_dir = u"WATERFALLS";
const std::filesystem::path macaddress_dir = u"MACADDRESS";
+1
View File
@@ -54,5 +54,6 @@ extern const std::filesystem::path ook_editor_dir;
extern const std::filesystem::path hopper_dir;
extern const std::filesystem::path subghz_dir;
extern const std::filesystem::path waterfalls_dir;
extern const std::filesystem::path macaddress_dir;
#endif /* __FILE_PATH_H__ */
+136 -1
View File
@@ -1,4 +1,3 @@
#include "theme.hpp"
namespace ui {
@@ -25,6 +24,9 @@ void Theme::SetTheme(ThemeId theme) {
case Red:
current = new ThemeRed();
break;
case Dark:
current = new ThemeDark();
break;
case DefaultGrey:
default:
current = new ThemeDefault();
@@ -725,4 +727,137 @@ ThemeRed::ThemeRed() {
bg_table_header = new Color{205, 30, 0};
}
ThemeDark::ThemeDark() {
bg_lightest = new Style{
.font = font::fixed_8x16,
.background = {32, 32, 32},
.foreground = Color::white(),
};
bg_lightest_small = new Style{
.font = font::fixed_5x8,
.background = {32, 32, 32},
.foreground = Color::white(),
};
bg_light = new Style{
.font = font::fixed_8x16,
.background = {24, 24, 24},
.foreground = Color::white(),
};
bg_medium = new Style{
.font = font::fixed_8x16,
.background = {16, 16, 16},
.foreground = Color::white(),
};
bg_dark = new Style{
.font = font::fixed_8x16,
.background = {8, 8, 8},
.foreground = Color::white(),
};
bg_darker = new Style{
.font = font::fixed_8x16,
.background = {4, 4, 4},
.foreground = Color::white(),
};
bg_darkest = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::white(),
};
bg_darkest_small = new Style{
.font = font::fixed_5x8,
.background = Color::black(),
.foreground = Color::white(),
};
bg_important_small = new Style{
.font = font::fixed_5x8,
.background = {64, 64, 64},
.foreground = Color::white(),
};
error_dark = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
warning_dark = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::yellow(),
};
ok_dark = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
fg_dark = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = {96, 96, 96},
};
fg_medium = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = {128, 128, 128},
};
fg_light = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::white(),
};
fg_red = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
fg_green = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
fg_yellow = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::yellow(),
};
fg_orange = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::orange(),
};
fg_blue = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::blue(),
};
fg_cyan = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::cyan(),
};
fg_darkcyan = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::dark_cyan(),
};
fg_magenta = new Style{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::magenta(),
};
option_active = new Style{
.font = font::fixed_8x16,
.background = {64, 64, 64},
.foreground = Color::white(),
};
status_active = new Color{0, 255, 0};
bg_table_header = new Color{48, 48, 48};
}
} // namespace ui
+6
View File
@@ -93,6 +93,11 @@ class ThemeRed : public ThemeTemplate {
ThemeRed();
};
class ThemeDark : public ThemeTemplate {
public:
ThemeDark();
};
class Theme {
public:
enum ThemeId {
@@ -101,6 +106,7 @@ class Theme {
Aqua = 2,
Green = 3,
Red = 4,
Dark = 5,
MAX
};
static ThemeTemplate* getInstance();
+43
View File
@@ -0,0 +1,43 @@
Copyright (C) 2025 HTotoo
# External UI elements
## Read carefully
These widgets are only used in external apps!
The concept is, we move these widgets out of FW space, and compile them with external apps. To all separately. This is multiple include, but since we compile the widget under different namespaces (and those namespaces will be under the external_app namespace) these all will be removed from the base firmware.
This way we can free up some spaces, and still reuse the same widgets in different apps easily.
## How to create external widget
You create a **hpp** file indet the ui/external folder, and include everything you need for your widget as you normally wanted to do.
**Don't forget the ifdef guards!**
**Never use any namespace for your class there!** The namespace of the actual ext app will be used where you include this.
Then create a **cpi** file, where you include your hpp file, and create the implementation of your widget class. Still, don't use namespace.
You won't need to include these files in any cmake file! (see usage, why)
## How to use these widgets
This is important, so follow the rules carefully. If it works, doesn't mean, you did it good!
In your external app's hpp file, you need to include the widget's hpp file.
**But be careful**, you must include it under the ext app's namespace, before your first class. For examlpe:
namespace ui::external_app::gfxeq {
#include "external/ui_grapheq.hpp"
class gfxEQView : public View {
Then you must include the cpi file in the external app's cpp file. Again, it must be under the ext app's namespace!
using namespace portapack;
namespace ui::external_app::gfxeq {
#include "external/ui_grapheq.cpi"
gfxEQView::gfxEQView(NavigationView& nav)
This must be done under each ext app that uses the same widget.
This way, the widget will be compiled multiple times under the external_app namespace, but those will be removed from the base. Each app will be able to use it, easy to handle, easy to create.
+221
View File
@@ -0,0 +1,221 @@
/*
* Copyright (C) 2025 RocketGod
* Copyright (C) 2025 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_grapheq.hpp"
/* GraphEq *************************************************************/
GraphEq::GraphEq(
Rect parent_rect,
bool clickable)
: Widget{parent_rect},
clickable_{clickable},
bar_heights(NUM_BARS, 0),
prev_bar_heights(NUM_BARS, 0) {
if (clickable) {
set_focusable(true);
// previous_data.resize(length_, 0);
}
}
void GraphEq::set_parent_rect(const Rect new_parent_rect) {
Widget::set_parent_rect(new_parent_rect);
calculate_params();
}
void GraphEq::calculate_params() {
y_top = screen_rect().top();
RENDER_HEIGHT = parent_rect().height();
BAR_WIDTH = (parent_rect().width() - (BAR_SPACING * (NUM_BARS - 1))) / NUM_BARS;
HORIZONTAL_OFFSET = screen_rect().left();
}
bool GraphEq::is_paused() const {
return paused_;
}
void GraphEq::set_paused(bool paused) {
paused_ = paused;
needs_background_redraw = true;
set_dirty();
}
bool GraphEq::is_clickable() const {
return clickable_;
}
void GraphEq::getAccessibilityText(std::string& result) {
result = paused_ ? "paused GraphEq" : "GraphEq";
}
void GraphEq::getWidgetName(std::string& result) {
result = "GraphEq";
}
bool GraphEq::on_key(const KeyEvent key) {
if (!clickable_) return false;
if (key == KeyEvent::Select) {
set_paused(!paused_);
if (on_select) {
on_select(*this);
}
return true;
}
return false;
}
bool GraphEq::on_keyboard(const KeyboardEvent key) {
if (!clickable_) return false;
if (key == 32 || key == 10) {
set_paused(!paused_);
if (on_select) {
on_select(*this);
}
return true;
}
return false;
}
bool GraphEq::on_touch(const TouchEvent event) {
if (!clickable_) return false;
switch (event.type) {
case TouchEvent::Type::Start:
focus();
return true;
case TouchEvent::Type::End:
set_paused(!paused_);
if (on_select) {
on_select(*this);
}
return true;
default:
return false;
}
}
void GraphEq::set_theme(Color base_color_, Color peak_color_) {
base_color = base_color_;
peak_color = peak_color_;
set_dirty();
}
void GraphEq::update_audio_spectrum(const AudioSpectrum& spectrum) {
const float bin_frequency_size = 48000.0f / 128;
for (int bar = 0; bar < NUM_BARS; bar++) {
float start_freq = FREQUENCY_BANDS[bar];
float end_freq = FREQUENCY_BANDS[bar + 1];
int start_bin = std::max(1, (int)(start_freq / bin_frequency_size));
int end_bin = std::min(127, (int)(end_freq / bin_frequency_size));
if (start_bin >= end_bin) {
end_bin = start_bin + 1;
}
float total_energy = 0;
int bin_count = 0;
for (int bin = start_bin; bin <= end_bin; bin++) {
total_energy += spectrum.db[bin];
bin_count++;
}
float avg_db = bin_count > 0 ? (total_energy / bin_count) : 0;
// Manually boost highs for better visual balance
float treble_boost = 1.0f;
if (bar == 10)
treble_boost = 1.7f;
else if (bar >= 9)
treble_boost = 1.3f;
else if (bar >= 7)
treble_boost = 1.3f;
// Mid emphasis for a V-shape effect
float mid_boost = 1.0f;
if (bar == 4 || bar == 5 || bar == 6) mid_boost = 1.2f;
float amplified_db = avg_db * treble_boost * mid_boost;
if (amplified_db > 255) amplified_db = 255;
float band_scale = 1.0f;
int target_height = (amplified_db * RENDER_HEIGHT * band_scale) / 255;
if (target_height > RENDER_HEIGHT) {
target_height = RENDER_HEIGHT;
}
// Adjusted to look nice to my eyes
float rise_speed = 0.8f;
float fall_speed = 1.0f;
if (target_height > bar_heights[bar]) {
bar_heights[bar] = bar_heights[bar] * (1.0f - rise_speed) + target_height * rise_speed;
} else {
bar_heights[bar] = bar_heights[bar] * (1.0f - fall_speed) + target_height * fall_speed;
}
}
set_dirty();
}
void GraphEq::paint(Painter& painter) {
if (!visible()) return;
if (!is_calculated) { // calc positions first
calculate_params();
is_calculated = true;
}
if (needs_background_redraw) {
painter.fill_rectangle(screen_rect(), Theme::getInstance()->bg_darkest->background);
needs_background_redraw = false;
}
if (paused_) {
return;
}
const int num_segments = RENDER_HEIGHT / SEGMENT_HEIGHT;
uint16_t bottom = screen_rect().bottom();
for (int bar = 0; bar < NUM_BARS; bar++) {
int x = HORIZONTAL_OFFSET + bar * (BAR_WIDTH + BAR_SPACING);
int active_segments = (bar_heights[bar] * num_segments) / RENDER_HEIGHT;
if (prev_bar_heights[bar] > active_segments) {
int clear_height = (prev_bar_heights[bar] - active_segments) * SEGMENT_HEIGHT;
int clear_y = bottom - prev_bar_heights[bar] * SEGMENT_HEIGHT;
painter.fill_rectangle({x, clear_y, BAR_WIDTH, clear_height}, Theme::getInstance()->bg_darkest->background);
}
for (int seg = 0; seg < active_segments; seg++) {
int y = bottom - (seg + 1) * SEGMENT_HEIGHT;
if (y < y_top) break;
Color segment_color = (seg >= active_segments - 2 && seg < active_segments) ? peak_color : base_color;
painter.fill_rectangle({x, y, BAR_WIDTH, SEGMENT_HEIGHT - 1}, segment_color);
}
prev_bar_heights[bar] = active_segments;
}
}
+88
View File
@@ -0,0 +1,88 @@
/*
* Copyright (C) 2025 RocketGod
* Copyright (C) 2025 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_GRAPHEQ_H__
#define __UI_GRAPHEQ_H__
#include "ui_widget.hpp"
class GraphEq : public Widget {
public:
std::function<void(GraphEq&)> on_select{};
GraphEq(Rect parent_rect, bool clickable = false);
GraphEq(const GraphEq&) = delete;
GraphEq(GraphEq&&) = delete;
GraphEq& operator=(const GraphEq&) = delete;
GraphEq& operator=(GraphEq&&) = delete;
bool is_paused() const;
void set_paused(bool paused);
bool is_clickable() const;
void paint(Painter& painter) override;
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void set_parent_rect(const Rect new_parent_rect) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
void update_audio_spectrum(const AudioSpectrum& spectrum);
void set_theme(Color base_color, Color peak_color);
private:
bool is_calculated{false};
bool paused_{false};
bool clickable_{false};
bool needs_background_redraw{true}; // Redraw background only when needed.
Color base_color = Color(255, 0, 255);
Color peak_color = Color(255, 255, 255);
std::vector<ui::Dim> bar_heights;
std::vector<ui::Dim> prev_bar_heights;
ui::Dim y_top = 2 * 16;
ui::Dim RENDER_HEIGHT = 288;
ui::Dim BAR_WIDTH = 20;
ui::Dim HORIZONTAL_OFFSET = 2;
static const int NUM_BARS = 11;
static const int BAR_SPACING = 2;
static const int SEGMENT_HEIGHT = 10;
static constexpr std::array<int16_t, NUM_BARS + 1> FREQUENCY_BANDS = {
375, // Bass warmth and low rumble (e.g., deep basslines, kick drum body)
750, // Upper bass punch (e.g., bass guitar punch, kick drum attack)
1500, // Lower midrange fullness (e.g., warmth in vocals, guitar body)
2250, // Midrange clarity (e.g., vocal presence, snare crack)
3375, // Upper midrange bite (e.g., instrument definition, vocal articulation)
4875, // Presence and edge (e.g., guitar bite, vocal sibilance start)
6750, // Lower brilliance (e.g., cymbal shimmer, vocal clarity)
9375, // Brilliance and air (e.g., hi-hat crispness, breathy vocals)
13125, // High treble sparkle (e.g., subtle overtones, synth shimmer)
16875, // Upper treble airiness (e.g., faint harmonics, room ambiance)
20625, // Top-end sheen (e.g., ultra-high harmonics, noise floor)
24375 // Extreme treble limit (e.g., inaudible overtones, signal cutoff, static)
};
void calculate_params(); // re calculate some parameters based on parent_rect()
};
#endif /*__UI_GRAPHEQ_H__*/
+2
View File
@@ -57,6 +57,8 @@ namespace ui {
#define UI_POS_HEIGHT_REMAINING(linenum) ((int)(screen_height - ((linenum)*UI_POS_DEFAULT_HEIGHT)))
// remaining px from the charnum-th character to the right of the screen
#define UI_POS_WIDTH_REMAINING(charnum) ((int)(screen_width - ((charnum)*UI_POS_DEFAULT_WIDTH)))
// px width of the screen
#define UI_POS_MAXHEIGHT (screen_height)
// Escape sequences for colored text; second character is index into term_colors[]
#define STR_COLOR_BLACK "\x1B\x00"
+200 -2
View File
@@ -1,6 +1,8 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2025 RocketGod
* Copyright (C) 2025 HTotoo
*
* This file is part of PortaPack.
*
@@ -2667,7 +2669,6 @@ bool Waveform::is_clickable() const {
}
void Waveform::getAccessibilityText(std::string& result) {
// no idea what this is in use in any places, but others have it
result = paused_ ? "paused waveform" : "waveform";
}
@@ -2689,7 +2690,6 @@ bool Waveform::on_key(const KeyEvent key) {
}
bool Waveform::on_keyboard(const KeyboardEvent key) {
// no idea what this is for, but others have it
if (!clickable_) return false;
if (key == 32 || key == 10) {
@@ -2822,6 +2822,204 @@ void Waveform::paint(Painter& painter) {
}
}
/* GraphEq *************************************************************/
GraphEq::GraphEq(
Rect parent_rect,
bool clickable)
: Widget{parent_rect},
clickable_{clickable},
bar_heights(NUM_BARS, 0),
prev_bar_heights(NUM_BARS, 0) {
if (clickable) {
set_focusable(true);
// previous_data.resize(length_, 0);
}
}
void GraphEq::set_parent_rect(const Rect new_parent_rect) {
Widget::set_parent_rect(new_parent_rect);
calculate_params();
}
void GraphEq::calculate_params() {
y_top = screen_rect().top();
RENDER_HEIGHT = parent_rect().height();
BAR_WIDTH = (parent_rect().width() - (BAR_SPACING * (NUM_BARS - 1))) / NUM_BARS;
HORIZONTAL_OFFSET = screen_rect().left();
}
bool GraphEq::is_paused() const {
return paused_;
}
void GraphEq::set_paused(bool paused) {
paused_ = paused;
needs_background_redraw = true;
set_dirty();
}
bool GraphEq::is_clickable() const {
return clickable_;
}
void GraphEq::getAccessibilityText(std::string& result) {
result = paused_ ? "paused GraphEq" : "GraphEq";
}
void GraphEq::getWidgetName(std::string& result) {
result = "GraphEq";
}
bool GraphEq::on_key(const KeyEvent key) {
if (!clickable_) return false;
if (key == KeyEvent::Select) {
set_paused(!paused_);
if (on_select) {
on_select(*this);
}
return true;
}
return false;
}
bool GraphEq::on_keyboard(const KeyboardEvent key) {
if (!clickable_) return false;
if (key == 32 || key == 10) {
set_paused(!paused_);
if (on_select) {
on_select(*this);
}
return true;
}
return false;
}
bool GraphEq::on_touch(const TouchEvent event) {
if (!clickable_) return false;
switch (event.type) {
case TouchEvent::Type::Start:
focus();
return true;
case TouchEvent::Type::End:
set_paused(!paused_);
if (on_select) {
on_select(*this);
}
return true;
default:
return false;
}
}
void GraphEq::set_theme(Color base_color_, Color peak_color_) {
base_color = base_color_;
peak_color = peak_color_;
set_dirty();
}
void GraphEq::update_audio_spectrum(const AudioSpectrum& spectrum) {
const float bin_frequency_size = 48000.0f / 128;
for (int bar = 0; bar < NUM_BARS; bar++) {
float start_freq = FREQUENCY_BANDS[bar];
float end_freq = FREQUENCY_BANDS[bar + 1];
int start_bin = std::max(1, (int)(start_freq / bin_frequency_size));
int end_bin = std::min(127, (int)(end_freq / bin_frequency_size));
if (start_bin >= end_bin) {
end_bin = start_bin + 1;
}
float total_energy = 0;
int bin_count = 0;
for (int bin = start_bin; bin <= end_bin; bin++) {
total_energy += spectrum.db[bin];
bin_count++;
}
float avg_db = bin_count > 0 ? (total_energy / bin_count) : 0;
// Manually boost highs for better visual balance
float treble_boost = 1.0f;
if (bar == 10)
treble_boost = 1.7f;
else if (bar >= 9)
treble_boost = 1.3f;
else if (bar >= 7)
treble_boost = 1.3f;
// Mid emphasis for a V-shape effect
float mid_boost = 1.0f;
if (bar == 4 || bar == 5 || bar == 6) mid_boost = 1.2f;
float amplified_db = avg_db * treble_boost * mid_boost;
if (amplified_db > 255) amplified_db = 255;
float band_scale = 1.0f;
int target_height = (amplified_db * RENDER_HEIGHT * band_scale) / 255;
if (target_height > RENDER_HEIGHT) {
target_height = RENDER_HEIGHT;
}
// Adjusted to look nice to my eyes
float rise_speed = 0.8f;
float fall_speed = 1.0f;
if (target_height > bar_heights[bar]) {
bar_heights[bar] = bar_heights[bar] * (1.0f - rise_speed) + target_height * rise_speed;
} else {
bar_heights[bar] = bar_heights[bar] * (1.0f - fall_speed) + target_height * fall_speed;
}
}
set_dirty();
}
void GraphEq::paint(Painter& painter) {
if (!visible()) return;
if (!is_calculated) { // calc positions first
calculate_params();
is_calculated = true;
}
if (needs_background_redraw) {
painter.fill_rectangle(screen_rect(), Theme::getInstance()->bg_darkest->background);
needs_background_redraw = false;
}
if (paused_) {
return;
}
const int num_segments = RENDER_HEIGHT / SEGMENT_HEIGHT;
uint16_t bottom = screen_rect().bottom();
for (int bar = 0; bar < NUM_BARS; bar++) {
int x = HORIZONTAL_OFFSET + bar * (BAR_WIDTH + BAR_SPACING);
int active_segments = (bar_heights[bar] * num_segments) / RENDER_HEIGHT;
if (prev_bar_heights[bar] > active_segments) {
int clear_height = (prev_bar_heights[bar] - active_segments) * SEGMENT_HEIGHT;
int clear_y = bottom - prev_bar_heights[bar] * SEGMENT_HEIGHT;
painter.fill_rectangle({x, clear_y, BAR_WIDTH, clear_height}, Theme::getInstance()->bg_darkest->background);
}
for (int seg = 0; seg < active_segments; seg++) {
int y = bottom - (seg + 1) * SEGMENT_HEIGHT;
if (y < y_top) break;
Color segment_color = (seg >= active_segments - 2 && seg < active_segments) ? peak_color : base_color;
painter.fill_rectangle({x, y, BAR_WIDTH, SEGMENT_HEIGHT - 1}, segment_color);
}
prev_bar_heights[bar] = active_segments;
}
}
/* VuMeter **************************************************************/
VuMeter::VuMeter(
+62
View File
@@ -1,6 +1,8 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2025 RocketGod
* Copyright (C) 2025 HTotoo
*
* This file is part of PortaPack.
*
@@ -1015,6 +1017,66 @@ class Waveform : public Widget {
bool if_ever_painted_pause{false}; // for prevent the "hidden" label keeps painting and being expensive
};
class GraphEq : public Widget {
public:
std::function<void(GraphEq&)> on_select{};
GraphEq(Rect parent_rect, bool clickable = false);
GraphEq(const GraphEq&) = delete;
GraphEq(GraphEq&&) = delete;
GraphEq& operator=(const GraphEq&) = delete;
GraphEq& operator=(GraphEq&&) = delete;
bool is_paused() const;
void set_paused(bool paused);
bool is_clickable() const;
void paint(Painter& painter) override;
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void set_parent_rect(const Rect new_parent_rect) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
void update_audio_spectrum(const AudioSpectrum& spectrum);
void set_theme(Color base_color, Color peak_color);
private:
bool is_calculated{false};
bool paused_{false};
bool clickable_{false};
bool needs_background_redraw{true}; // Redraw background only when needed.
Color base_color = Color(255, 0, 255);
Color peak_color = Color(255, 255, 255);
std::vector<ui::Dim> bar_heights;
std::vector<ui::Dim> prev_bar_heights;
ui::Dim y_top = 2 * 16;
ui::Dim RENDER_HEIGHT = 288;
ui::Dim BAR_WIDTH = 20;
ui::Dim HORIZONTAL_OFFSET = 2;
static const int NUM_BARS = 11;
static const int BAR_SPACING = 2;
static const int SEGMENT_HEIGHT = 10;
static constexpr std::array<int16_t, NUM_BARS + 1> FREQUENCY_BANDS = {
375, // Bass warmth and low rumble (e.g., deep basslines, kick drum body)
750, // Upper bass punch (e.g., bass guitar punch, kick drum attack)
1500, // Lower midrange fullness (e.g., warmth in vocals, guitar body)
2250, // Midrange clarity (e.g., vocal presence, snare crack)
3375, // Upper midrange bite (e.g., instrument definition, vocal articulation)
4875, // Presence and edge (e.g., guitar bite, vocal sibilance start)
6750, // Lower brilliance (e.g., cymbal shimmer, vocal clarity)
9375, // Brilliance and air (e.g., hi-hat crispness, breathy vocals)
13125, // High treble sparkle (e.g., subtle overtones, synth shimmer)
16875, // Upper treble airiness (e.g., faint harmonics, room ambiance)
20625, // Top-end sheen (e.g., ultra-high harmonics, noise floor)
24375 // Extreme treble limit (e.g., inaudible overtones, signal cutoff, static)
};
void calculate_params(); // re calculate some parameters based on parent_rect()
};
class VuMeter : public Widget {
public:
VuMeter(Rect parent_rect, uint32_t LEDs, bool show_max);
+1 -2
View File
@@ -3,6 +3,5 @@
Licensed under [GNU GPL v3](../../../LICENSE)
USAGE:
- Copy file from: https://raw.githubusercontent.com/kx1t/planefence-airlinecodes/main/airlinecodes.txt
- Run Python 3 script: `./make_airlines_db.py`
- Copy file to /ADSB folder on SDCARD
- Move file "airlines.db" to /ADSB folder on SDCARD
@@ -325,7 +325,7 @@ AMT,ATA Airlines,,United States
AMU,Air Macau,AIR MACAO,Macao
AMV,AMC Airlines,,Egypt
AMW,Air Midwest,,United States
AMX,Aeroméxico,AEROMEXICO,Mexico
AMX,Aeromexico,AEROMEXICO,Mexico
AMY,Air Ambar,AIR AMBAR,Dominican Republic
AMZ,Amiya Airline,AMIYA AIR,Nigeria
ANA,All Nippon Airways,ALL NIPPON,Japan
@@ -408,7 +408,7 @@ ARC,Air Routing International Corp.,,United States
ARD,Aerocondor,,Portugal
ARE,Aires Aerovías de Integración Regional S.A.,AIRES,Colombia
ARF,Aero Flight,,Germany
ARG,Aerolíneas Argentinas,ARGENTINA,Argentina
ARG,Aerolineas Argentinas,ARGENTINA,Argentina
ARH,Aerohelicopteros,AEROHELCA,Venezuela
ARI,Aero Vics,AEROVICS,Mexico
ARJ,Aerojet de Costa Rica S.A.,,Costa Rica
@@ -820,7 +820,7 @@ BNT,Bentiu Air Transport,BENTIU AIR,Sudan
BNV,Benane Aviation Corporation,BENANE,Mauritania
BNW,British North West Airlines,BRITISH NORTH,United Kingdom
BNX,LAI - Línea Aérea IAACA,AIR BARINAS,Venezuela
BNZ,Aerolíneas Bonanza,AERO BONANZA,Mexico
BNZ,Bonza,BONZA,Australia
BOA,Boniair,KUMANOVO,Macedonia
BOB,Backbone A/S,BACKBONE,Denmark
BOC,Aerobona,AEROBONA,Mexico
@@ -1465,6 +1465,7 @@ DET,DETA Air,SAMAL,Kazakhstan
DEV,Red Devils Parachute Display Team,RED DEVILS,United Kingdom
DFA,Aero Coach Aviation,AERO COACH,United States
DFC,Aeropartner,DARK BLUE,Czech Republic
DFL,Babcock AirAmbulance,MEDIFLIGHT,Sweden
DFS,Dwyer Aircraft Services,DWYAIR,United States
DGA,Yellow River Delta General Aviation,YELLOW RIVER,China
DGO,DGO Jet,DGO JET,Mexico
@@ -1592,8 +1593,10 @@ DVA,Discovery Airways,DISCOVERY AIRWAYS,United States
DVB,Don Avia,DONSEBAI,Kazakhstan
DVI,Aero Davinci International,AERO DAVINCI,Mexico
DVN,Adventia,,Spain
DVY,Legends Airways,DEVIL RAY,United States
DWA,Dense Airways,,United States
DWD,Deutsche Luftverkehrsgesellschaft (DLT),,Germany
DWI,Arajet,DOMINICAN,Dominican Republic
DWN,Dawn Air,DAWN AIR,United States
DWR,Delaware Skyways,DELAWARE,United States
DWT,Darwin Airline,DARWIN,Switzerland
@@ -1905,6 +1908,7 @@ EZJ,Ezjet GT,GUYANA JET,Guyana
EZS,easyJet Switzerland,TOPSWISS,Switzerland
EZX,Eagle Express Air Charter,EAGLEXPRESS,Malaysia
EZY,easyJet UK,EASY,United Kingdom
EZZ,ETF Airways,ENTERPRIZE,Croatia
FAB,First Air,FIRST AIR,Canada
FAC,Atlantic Helicopters,FAROECOPTER,Denmark
FAD,Rog-Air,AIR FRONTIER,Canada
@@ -2034,7 +2038,7 @@ FJI,Fiji Airways,PACIFIC,Fiji
FJK,Fly Jet Kz,,Kazakhstan
FJM,Fly Jamaica Airways,GREENHEART,Jamaica
FJO,Flexjet Operations Malta Limited,FLEX MALTA,Malta
FRJ,Fly Jordan,SOLITAIRE AIR,Jordan
FJR,Fly Jordan,SOLITAIRE AIR,Jordan
FJS,Florida Jet Service,FLORIDAJET,United States
FKA,Flying kangaroo Airline,,Australia
FKI,FLM Aviation Mohrdieck,KIEL AIR,Germany
@@ -2397,7 +2401,7 @@ GPL,DLR,GERMAN POLAR,Germany
GPM,Grup Air-Med,GRUPOMED,Spain
GPR,GPM Aeroservicio,GPM AEROSERVICIO,Mexico
GPX,GP Aviation,,Bulgaria
GRA,Guardian Air Asset Management,FLEX,South Africa
GRA,Costa Rica Green Airways,,,Costa Rica
GRB,Phoenix Air Group,GREY BIRD,United States
GRD,National Grid plc,GRID,United Kingdom
GRE,Air Scotland,GREECE AIRWAYS,Greece
@@ -2459,7 +2463,7 @@ GWN,Gwyn Aviation,GWYN,United Kingdom
GWR,Aura Airlines,LEMON,Spain
GWS,General Airways,GENAIR,South Africa
GWY,USA3000 Airlines,GETAWAY,United States
GXA,Grixona,GRIXONA,Moldova
GXA,GlobalX Airlines,GEMINI,United States
GXG,GermanXL,,Germany
GXL,Star XL German Airlines,STARDUST,Germany
GXY,Galaxy Airlines,GALAX,Japan
@@ -2608,6 +2612,7 @@ HMC,Heliamerica De Mexico,HELIAMERICA,Mexico
HMD,Charlie Hammonds Flying Service,HAMMOND,United States
HME,Babcock MCS,HELIMED,United Kingdom
HMF,Norrlandsflyg,LIFEGUARD SWEDEN,Sweden
HMJ,Harmony Jets,HARMONG JETS,Malta
HMM,Hamra Air,HAMRA,United Arab Emirates
HMP,Papair Terminal,PAPAIR TERMINAL,Haiti
HMR,North American Charters,HAMMER,Canada
@@ -2623,6 +2628,7 @@ HNR,Haiti National Airlines (HANA),HANAIR,Haiti
HNT,Helicopteros Internacionales,HELICOP INTER,Mexico
HNX,Hankook Airline,,South Korea
HOA,Hola Airlines,HOLA,Spain
HOB,HL Aircraft LLC,HOBBY JET,United States
HOG,Mahogany Air Charters,HOGAN AIR,Zambia
HOL,Holiday Airlines (US Airline),HOLIDAY,United States
HOM,Aero Homex,AERO HOMEX,Mexico
@@ -3077,7 +3083,7 @@ JSM,Jet Stream,,Moldova
JSN,Suncor Energy Inc,JETSUN,Canada
JSP,Palmer Aviation,PALMER,United Kingdom
JSR,Jusur airways,,Egypt
JST,Eastern Australia Airlines,,Australia
JST,Jetstar Airways,,Australia
JSV,Japan Aircraft Service,,Japan
JSW,Jigsaw Project,,United Kingdom
JSX,JetSuiteX Air,BIGSTRIPE,United States
@@ -3617,6 +3623,7 @@ MAK,MAT Macedonian Airlines,MAKAVIO,Macedonia
MAL,Morningstar Air Express,MORNINGSTAR,Canada
MAM,Aeródromo De La Mancha,AEROMAN,Spain
MAN,Mannion Air Charter,MANNION,United States
MAO,Mayo Clinic,MAYO,United States
MAQ,Mac Aviation,MAC AVIATION,Spain
MAR,March Helicopters,MARCH,United Kingdom
MAS,Malaysia Airlines,MALAYSIAN,Malaysia
@@ -3633,6 +3640,7 @@ MBC,Airjet Exploracao Aerea de Carga,MABECO,Angola
MBE,Martin-Baker,MARTIN,United Kingdom
MBG,Zephyr Aviation,CHALGROVE,United Kingdom
MBI,Mountain Bird,MOUNTAIN BIRD,United States
MBK,Chrono Jet,MATBLACK,Canada
MBL,First City Air,FIRST CITY,United Kingdom
MBN,Zambian Airways,ZAMBIANA,Zambia
MBO,Mobil Oil,MOBIL,Canada
@@ -4441,6 +4449,7 @@ PDI,Paradise Island Airways,PARADISE ISLAND,United States
PDQ,PDQ Air Charter,DISPATCH,United States
PDR,COMAV,SPEEDSTER,Namibia
PDT,Piedmont Airlines,PIEDMONT,United States
PDU,Purdue University,PURDUE,United States
PDV,Elicar,ELICAR,Italy
PDY,Pen-Avia,PENDLEY,United Kingdom
PEA,Pan Europeenne Air Service,,France
@@ -4564,6 +4573,7 @@ PNH,Panh,KUBAN LIK,Russia
PNK,Air Pink,AIRPINK,Serbia
PNL,Aero Personal,AEROPERSONAL,Mexico
PNM,Panorama,PANORAMA,Spain
PNO,Panorama Aviation,PANO,Canada
PNP,Pineapple Air,PINEAPPLE AIR,Bahamas
PNR,PAN Air,SKYJET,Spain
PNS,Survey Udara (Penas),PENAS,Indonesia
@@ -4931,6 +4941,7 @@ RNB,Rosneft-Baltika,ROSBALT,Russia
RND,Rutland Aviation,RUTLAND,United Kingdom
RNE,Air Salone,AIR SALONE,Sierra Leone
RNG,Orange Aircraft Leasing,ORANGE,Netherlands
RNI,Rennia Aviation,RENNIA,United States
RNM,Aeronem Air Cargo,AEROMNEM,Ecuador
RNR,Air Cargo Masters,RUNNER,United States
RNS,Ronso,RONSO,Mexico
@@ -5216,6 +5227,7 @@ SFU,Solent Flight,SAINTS,United Kingdom
SFW,Safi Airways,SAFI,AF
SFX,S.K. Logistics,SWAMP FOX,United States
SFY,Gulf Flite Center,SKY FLITE,United States
SFZ,Pionair,SKYFORCE,Australia
SGA,Air Saigon,AIR SAIGON,Vietnam
SGB,Sky King Inc.,SONGBIRD,United States
SGC,SGC Aviation,SAINT GEORGE,Austria
@@ -5270,7 +5282,7 @@ SIE,Sierra Express,SEREX,United States
SIH,Skynet Airlines,BLUEJET,Ireland
SII,Aero Servicios Ejecutivos Internacionales,ASEISA,Mexico
SIJ,Seco International,,Japan
SIL,Servicios Aeronáuticos Integrales,SERVICIOS INTEGRALES,Mexico
SIL,Silver Airways,SILVER WINGS,United States
SIM,Star Air,,Sierra Leone
SIO,Sirio,SIRIO,Italy
SIP,Air Spirit,AIR SPIRIT,United States
@@ -5802,6 +5814,7 @@ TKC,Tikal Jets Airlines,TIKAL,Guatemala
TKE,Take Air Line,ISLAND BIRD,France
TKJ,Tarkim Aviation,TARKIM AVIATION,Turkey
TKK,FlyADVANCED,,United States
TKM,JM Family Aviation,TACOMA,United States
TKR,Canadian Interagency Forest Fire Centre,TANKER,Canada
TKS,Tomsk-Avia,,Russia
TKX,Tropical International Airways,TROPEXPRESS,Saint Kitts and Nevis
@@ -5827,6 +5840,7 @@ TLW,Teamline Air,Teamline,Austria
TLX,Telesis Transair,TELESIS,United States
TLY,Top Fly,TOPFLY,Spain
TMA,Trans Mediterranean Airlines,TANGO LIMA,Lebanon
TMB,Volato,TOMBO,United States
TMC,Travel Management Company,TRAIL BLAZER,United States
TMD,Transmandu,TRANSMANDU,Venezuela
TME,Aero Taxi del Centro de Mexico,TAXICENTRO,Mexico
@@ -6086,6 +6100,7 @@ UDC,DonbassAero,DONBASS AERO,Ukraine
UDN,Dniproavia,DNIEPRO,Ukraine
UEA,United Eagle Airlines,UNITED EAGLE,China
UED,Air LA,AIR L-A,United States
UEE,United Eagle,EAGLE SLOVENIA,Slovenia
UEJ,Jetcorp,,United States
UES,Ues-Avia Aircompany,AVIASYSTEM,Ukraine
UEU,United European Airlines,UNITED EUROPEAN,Romania
@@ -6200,7 +6215,7 @@ VAL,Voyageur Airways,VOYAGEUR,Canada
VAM,Ameravia,AMERAVIA,Uruguay
VAN,Caravan Air,CAMEL,Mauritania
VAP,Phuket Air,PHUKET AIR,Thailand
VAR,Veca Airlines,VECA,El Salvador
VAR,United Aviate Academy,VARNEY,United States
VAS,ATRAN Cargo Airlines,ATRAN,Russian Federation
VAT,Visionair,VISIONAIR,Ireland
VAU,V Australia Airlines,,Australia
@@ -6278,7 +6293,7 @@ VIS,Vision Air International,,Pakistan
VIT,Aviastar Mandiri,AVIASTAR,Indonesia
VIV,Aeroenlaces Nacionales,AEROENLACES,Mexico
VIZ,Aerovis Airlines,AEROVIZ,Ukraine
VJA,ValuJet Airlines,CRITTER,United States
VJA,Vista America,ICONIC,United States
VJC,Vietjet Air,VIETJET,Vietnam
VJE,AvJet Routing,,United Arab Emirates
VJM,Viajes Ejecutivos Mexicanos,VIAJES MEXICANOS,Mexico
@@ -6320,7 +6335,7 @@ VNG,Aero Servicios Vanguardia,VANGUARDIA,Mexico
VNK,Vipport Joint Stock Company,,Russia
VNL,Vanilla Air,VANILLA,Japan
VNP,Virgin Pacific,,Fiji
VNT,Avient Air Zambia,AVIENT,Zambia
VNT,Ventura,VENTURA,United States
VNX,Fly Advance,VANCE,United States
VNZ,Tbilaviamsheni,TBILAVIA,Georgia
VOA,Viaggio Air,VIAGGIO,Bulgaria
@@ -6331,7 +6346,7 @@ VOI,Volaris,VOLARIS,Mexico
VOL,Blue Chip Jet,BLUE SPEED,Sweden
VOO,Volotea,,Spain
VOR,Flight Calibration Services Ltd.,FLIGHT CAL,United Kingdom
VOS,Rovos Air,ROVOS,South Africa
VOS,Volaris El Salvador,JETSAL,South Africa
VOZ,Virgin Australia,VELOCITY,Australia
VPA,DanubeWings,VIP TAXI,Slovakia
VPB,Veteran Air,VETERAN,Ukraine
@@ -6422,7 +6437,7 @@ WAU,Wizz Air Ukraine,,Ukraine
WAV,Warbelow's Air Ventures,WARBELOW,United States
WAW,Wings Airways,WING SHUTTLE,United States
WAY,Airways,GARONNE,France
WAZ,Wizz Air,WIZZ SKY,United Arab Emirates
WAZ,Wizz Air Abu Dabi,WIZZ SKY,United Arab Emirates
WBA,Finncomm Airlines,WESTBIRD,Finland
WBR,Multi-Aero,WEBER,United States
WCA,West Coast Airways,WEST-LEONE,Sierra Leone
@@ -6499,6 +6514,8 @@ WLX,West Air Luxembourg,WEST LUX,Luxembourg
WMA,Watermakers Air,WATERMAKERS,United States
WML,Chantilly Air,MARLIN,United States
WMN,Trident Aircraft,WATERMAN,United States
WMT,Wizz Air Malta,WIZZAIR MALTA,Malta
WMU,W.M. Uni. College of Aviation,SKYBRONCO,United States
WNA,Winair,WINAIR,United States
WNR,Wondair on Demand Aviation,WONDAIR,Spain
WOA,World Airways,WORLD,United States
@@ -6535,6 +6552,7 @@ WTP,Westpoint Air,WESTPOINT,Canada
WTV,Western Aviators,WESTAVIA,United States
WUE,Adolf Wurth GmbH,FASTY,Germany
WUK,Wizz Air UK,WIZZ GO,United Kingdom
WUP,Wheels Up,UPJET,United States
WVA,Hand D Aviation,WABASH VALLEY,United States
WVL,Wizz Air Bulgaria,WIZZBUL,Bulgaria
WVZ,Winair,,HR
@@ -6548,7 +6566,7 @@ WYC,Wycombe Air Centre,WYCOMBE,United Kingdom
WYG,Wyoming Airlines,WYOMING,United States
WYT,2 Sqn No 1 Elementary Flying Training School,WYTON,United Kingdom
WZP,Zip,ZIPPER,Canada
WZZ,Wizz Air,WIZZAIR,Hungary
WZZ,Wizz Air Hungary,WIZZAIR,Hungary
X9F,FRA Air,,Germany
XAA,Aeronautical Radio Inc,,United States
XAB,Xabre Aerolineas,AERO XABRE,Mexico
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# Copyright (C) 2021 ArjanOnwezen
# Copyright (C) 2025 Tommaso Ventafridda
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,26 +23,251 @@
# as a source.
# -------------------------------------------------------------------------------------
import csv
import os
import shutil
import unicodedata
icao_codes=bytearray()
airlines_countries=bytearray()
row_count=0
database=open("airlines.db", "wb")
with open('airlinecodes.txt', 'rt', encoding="utf-8") as csv_file:
sorted_lines=sorted(csv_file.readlines()[1:])
for row in csv.reader(sorted_lines, quotechar='"', delimiter=',', quoting=csv.QUOTE_ALL, skipinitialspace=True):
icao_code=row[0]
# Normalize some unicode characters
airline=unicodedata.normalize('NFKD', row[1][:32]).encode('ascii', 'ignore')
country=unicodedata.normalize('NFKD', row[3][:32]).encode('ascii', 'ignore')
if len(icao_code) == 3 :
airline_padding=bytearray()
country_padding=bytearray()
icao_codes=icao_codes+bytearray(icao_code+'\0', encoding='ascii')
airline_padding=bytearray('\0' * (32 - len(airline)), encoding='ascii')
country_padding=bytearray('\0' * (32 - len(country)), encoding='ascii')
airlines_countries=airlines_countries+bytearray(airline+airline_padding+country+country_padding)
row_count+=1
database.write(icao_codes+airlines_countries)
print("Total of", row_count, "ICAO codes stored in database")
import urllib.request
from dataclasses import dataclass
from typing import Dict, Set, Tuple
@dataclass
class AirlineRecord:
"""Represents an airline record with all relevant fields"""
icao_code: str
airline: str
country: str
def __hash__(self):
return hash(self.icao_code)
def __eq__(self, other):
if not isinstance(other, AirlineRecord):
return False
return self.icao_code == other.icao_code
def data_equals(self, other):
"""Check if all data fields are equal (excluding ICAO code which is the key)"""
if not isinstance(other, AirlineRecord):
return False
return self.airline == other.airline and self.country == other.country
def backup_previous_file() -> bool:
"""Backup the existing airlinecodes.txt file if it exists. Returns True if backup was made."""
if os.path.exists("airlinecodes.txt"):
shutil.copy2("airlinecodes.txt", "airlinecodes_previous.txt")
print("Backed up previous airline codes file")
return True
return False
def download_airline_codes() -> None:
"""Download the airline codes file from GitHub"""
url = "https://raw.githubusercontent.com/kx1t/planefence-airlinecodes/main/airlinecodes.txt"
print(f"Downloading airline codes database from {url}...")
try:
urllib.request.urlretrieve(url, "airlinecodes.txt")
print("Download completed successfully.")
except Exception as e:
print(f"Error downloading airline codes file: {e}")
raise
def parse_airline_file(filename: str) -> Dict[str, AirlineRecord]:
"""Parse airline codes file and return a dictionary of records"""
records = {}
if not os.path.exists(filename):
return records
try:
with open(filename, "rt", encoding="utf-8") as csv_file:
sorted_lines = sorted(csv_file.readlines()[1:])
for row in csv.reader(
sorted_lines,
quotechar='"',
delimiter=",",
quoting=csv.QUOTE_ALL,
skipinitialspace=True,
):
if len(row) >= 4: # Ensure we have enough columns
icao_code = row[0]
# Normalize some unicode characters
airline = (
unicodedata.normalize("NFKD", row[1][:32])
.encode("ascii", "ignore")
.decode("ascii")
)
country = (
unicodedata.normalize("NFKD", row[3][:32])
.encode("ascii", "ignore")
.decode("ascii")
)
if len(icao_code) == 3:
records[icao_code] = AirlineRecord(
icao_code=icao_code, airline=airline, country=country
)
except Exception as e:
print(f"Warning: Could not parse airline file {filename}: {e}")
return {}
return records
def compare_records(
old_records: Dict[str, AirlineRecord], new_records: Dict[str, AirlineRecord]
) -> Tuple[Set[str], Set[str], Set[str]]:
"""Compare old and new records, return sets of new, deleted, and changed ICAO codes"""
old_keys = set(old_records.keys())
new_keys = set(new_records.keys())
new_icao_codes = new_keys - old_keys
deleted_icao_codes = old_keys - new_keys
# Check for changes in existing records
changed_icao_codes = set()
for icao in old_keys & new_keys:
if not old_records[icao].data_equals(new_records[icao]):
changed_icao_codes.add(icao)
return new_icao_codes, deleted_icao_codes, changed_icao_codes
def write_database(records: Dict[str, AirlineRecord]) -> int:
"""Write records to database file using original format"""
icao_codes = bytearray()
airlines_countries = bytearray()
row_count = 0
# Sort by ICAO code for consistency
sorted_records = sorted(records.values(), key=lambda r: r.icao_code)
for record in sorted_records:
# Normalize and encode data (same as original)
airline = unicodedata.normalize("NFKD", record.airline[:32]).encode(
"ascii", "ignore"
)
country = unicodedata.normalize("NFKD", record.country[:32]).encode(
"ascii", "ignore"
)
# Add ICAO code with null terminator (original format)
icao_codes = icao_codes + bytearray(record.icao_code + "\0", encoding="ascii")
# Add padded data fields (original format)
airline_padding = bytearray("\0" * (32 - len(airline)), encoding="ascii")
country_padding = bytearray("\0" * (32 - len(country)), encoding="ascii")
airlines_countries = airlines_countries + bytearray(
airline + airline_padding + country + country_padding
)
row_count += 1
with open("airlines.db", "wb") as database:
database.write(icao_codes + airlines_countries)
return row_count
def create_database() -> None:
"""Create the airline database from the downloaded file"""
# Backup existing file before downloading new one
has_previous = backup_previous_file()
# Download new file
download_airline_codes()
# Parse both files for comparison
old_records = {}
if has_previous:
print("Parsing previous airline codes file...")
old_records = parse_airline_file("airlinecodes_previous.txt")
print(f"Found {len(old_records)} records in previous file")
print("Parsing new airline codes file...")
new_records = parse_airline_file("airlinecodes.txt")
print(f"Found {len(new_records)} records in new file")
# Compare records if we have a previous version
if old_records:
new_icao_codes, deleted_icao_codes, changed_icao_codes = compare_records(
old_records, new_records
)
# Print change statistics
print("\n" + "=" * 50)
print("AIRLINE CODES CHANGE SUMMARY")
print("=" * 50)
print(f"New records: {len(new_icao_codes):>8}")
print(f"Deleted records: {len(deleted_icao_codes):>8}")
print(f"Changed records: {len(changed_icao_codes):>8}")
print(f"Total records: {len(new_records):>8}")
print("=" * 50)
# Show examples of changes (limited to avoid spam)
if new_icao_codes and len(new_icao_codes) <= 15:
print(f"\nNew ICAO codes: {', '.join(sorted(new_icao_codes))}")
elif new_icao_codes:
sample_new = sorted(list(new_icao_codes))[:10]
print(
f"\nSample new ICAO codes: {', '.join(sample_new)} (and {len(new_icao_codes)-10} more)"
)
if deleted_icao_codes and len(deleted_icao_codes) <= 15:
print(f"Deleted ICAO codes: {', '.join(sorted(deleted_icao_codes))}")
elif deleted_icao_codes:
sample_deleted = sorted(list(deleted_icao_codes))[:10]
print(
f"Sample deleted ICAO codes: {', '.join(sample_deleted)} (and {len(deleted_icao_codes)-10} more)"
)
if changed_icao_codes and len(changed_icao_codes) <= 15:
print(f"Changed ICAO codes: {', '.join(sorted(changed_icao_codes))}")
elif changed_icao_codes:
sample_changed = sorted(list(changed_icao_codes))[:10]
print(
f"Sample changed ICAO codes: {', '.join(sample_changed)} (and {len(changed_icao_codes)-10} more)"
)
# Show some specific examples of changes
if changed_icao_codes:
print(f"\nExample changes:")
for icao in sorted(list(changed_icao_codes))[:3]:
old_rec = old_records[icao]
new_rec = new_records[icao]
if old_rec.airline != new_rec.airline:
print(
f" {icao}: Airline '{old_rec.airline}''{new_rec.airline}'"
)
if old_rec.country != new_rec.country:
print(
f" {icao}: Country '{old_rec.country}''{new_rec.country}'"
)
else:
print(
"\nNo previous airline codes file found - this appears to be the first run"
)
# Create database from new records
print("\nCreating airline database...")
row_count = write_database(new_records)
print("Total of", row_count, "ICAO codes stored in database")
def cleanup_temp() -> None:
"""Cleanup temporary files created during the process"""
if os.path.exists("airlinecodes_previous.txt"):
os.remove("airlinecodes_previous.txt")
if __name__ == "__main__":
try:
create_database()
cleanup_temp()
except Exception as e:
print(f"Error creating airline database: {e}")
else:
print("Airline database created successfully.")
+1 -2
View File
@@ -3,6 +3,5 @@
Licensed under [GNU GPL v3](../../../LICENSE)
USAGE:
- Copy file from: https://opensky-network.org/datasets/metadata/aircraftDatabase.csv
- Run Python 3 script: `./make_icao24_db.py`
- Copy file to /ADSB folder on SDCARD
- Move "icao24.db" file to /ADSB folder on SDCARD
File diff suppressed because it is too large Load Diff
+267 -34
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
# Copyright (C) 2021 ArjanOnwezen
# Copyright (C) 2025 Tommaso Ventafridda
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,43 +21,275 @@
# -------------------------------------------------------------------------------------
# Create icao24.db, used for ADS-B receiver application, using
# https://opensky-network.org/datasets/metadata/aircraftDatabase.csv
# https://opensky-network.org/datasets/metadata/aircraftDatabase.csv
# as a source.
# -------------------------------------------------------------------------------------
import csv
import unicodedata
icao24_codes=bytearray()
data=bytearray()
row_count=0
import os
import shutil
import urllib.request
from dataclasses import dataclass
from typing import Dict, Set, Tuple
database=open("icao24.db", "wb")
with open('aircraftDatabase.csv', 'rt') as csv_file:
sorted_lines=sorted(csv_file.readlines()[1:])
for row in csv.reader(sorted_lines, quotechar='"', delimiter=',', quoting=csv.QUOTE_ALL, skipinitialspace=True):
# only store in case enough info is available
if len(row) == 27 and len(row[0]) == 6 and len(row[1]) > 0:
icao24_code=row[0][:6].upper()
registration=row[1][:8].encode('ascii', 'ignore')
manufacturer=row[3][:32].encode('ascii', 'ignore')
model=row[4][:32].encode('ascii', 'ignore')
# in case icao aircraft type isn't, use ac type like BALL for balloon
if len(row[8]) == 3:
actype=row[8][:3].encode('ascii', 'ignore')
else:
actype=row[5][:4].encode('ascii', 'ignore')
owner=row[13][:32].encode('ascii', 'ignore')
operator=row[9][:32].encode('ascii', 'ignore')
#padding
icao24_codes.extend(bytearray(icao24_code+'\0', encoding='ascii'))
registration_padding=bytearray('\0' * (9 - len(registration)), encoding='ascii')
manufacturer_padding=bytearray('\0' * (33 - len(manufacturer)), encoding='ascii')
model_padding=bytearray('\0' * (33 - len(model)), encoding='ascii')
actype_padding=bytearray('\0' * (5 - len(actype)), encoding='ascii')
owner_padding=bytearray('\0' * (33 - len(owner)), encoding='ascii')
operator_padding=bytearray('\0' * (33 - len(operator)), encoding='ascii')
data.extend(bytearray(registration+registration_padding+manufacturer+manufacturer_padding+model+model_padding+actype+actype_padding+owner+owner_padding+operator+operator_padding))
row_count+=1
database.write(icao24_codes+data)
print("Total of", row_count, "ICAO codes stored in database")
@dataclass
class AircraftRecord:
"""Represents an aircraft record with all relevant fields"""
icao24: str
registration: str
manufacturer: str
model: str
actype: str
owner: str
operator: str
def __hash__(self):
return hash(self.icao24)
def __eq__(self, other):
if not isinstance(other, AircraftRecord):
return False
return self.icao24 == other.icao24
def data_equals(self, other):
"""Check if all data fields are equal (excluding ICAO24 which is the key)"""
if not isinstance(other, AircraftRecord):
return False
return (
self.registration == other.registration
and self.manufacturer == other.manufacturer
and self.model == other.model
and self.actype == other.actype
and self.owner == other.owner
and self.operator == other.operator
)
def backup_previous_csv() -> bool:
"""Backup the existing CSV file if it exists. Returns True if backup was made."""
if os.path.exists("aircraftDatabase.csv"):
shutil.copy2("aircraftDatabase.csv", "aircraftDatabase_previous.csv")
print("Backed up previous CSV file")
return True
return False
def download_aircraft_database() -> None:
"""Download the aircraft database file from OpenSky Network"""
url = "https://opensky-network.org/datasets/metadata/aircraftDatabase.csv"
print(f"Downloading aircraft database from {url}...")
try:
urllib.request.urlretrieve(url, "aircraftDatabase.csv")
print("Download completed successfully.")
except Exception as e:
print(f"Error downloading aircraft database file: {e}")
raise
def parse_csv_file(filename: str) -> Dict[str, AircraftRecord]:
"""Parse CSV file and return a dictionary of records"""
records = {}
if not os.path.exists(filename):
return records
try:
with open(filename, "rt", encoding="utf-8") as csv_file:
sorted_lines = sorted(csv_file.readlines()[1:])
for row in csv.reader(
sorted_lines,
quotechar='"',
delimiter=",",
quoting=csv.QUOTE_ALL,
skipinitialspace=True,
):
# only store in case enough info is available
if len(row) == 27 and len(row[0]) == 6 and len(row[1]) > 0:
icao24_code = row[0][:6].upper()
registration = row[1][:8]
manufacturer = row[3][:32]
model = row[4][:32]
# in case icao aircraft type isn't, use ac type like BALL for balloon
if len(row[8]) == 3:
actype = row[8][:3]
else:
actype = row[5][:4]
owner = row[13][:32]
operator = row[9][:32]
records[icao24_code] = AircraftRecord(
icao24=icao24_code,
registration=registration,
manufacturer=manufacturer,
model=model,
actype=actype,
owner=owner,
operator=operator,
)
except Exception as e:
print(f"Warning: Could not parse CSV file {filename}: {e}")
return {}
return records
def compare_records(
old_records: Dict[str, AircraftRecord], new_records: Dict[str, AircraftRecord]
) -> Tuple[Set[str], Set[str], Set[str]]:
"""Compare old and new records, return sets of new, deleted, and changed ICAO codes"""
old_keys = set(old_records.keys())
new_keys = set(new_records.keys())
new_icao_codes = new_keys - old_keys
deleted_icao_codes = old_keys - new_keys
# Check for changes in existing records
changed_icao_codes = set()
for icao in old_keys & new_keys:
if not old_records[icao].data_equals(new_records[icao]):
changed_icao_codes.add(icao)
return new_icao_codes, deleted_icao_codes, changed_icao_codes
def write_database(records: Dict[str, AircraftRecord]) -> int:
"""Write records to database file using original format"""
# bytearrays to store icao24 codes and data
icao24_codes = bytearray()
data = bytearray()
row_count = 0
# Sort by ICAO code for consistency
sorted_records = sorted(records.values(), key=lambda r: r.icao24)
for record in sorted_records:
# Encode data fields
registration = record.registration.encode("ascii", "ignore")
manufacturer = record.manufacturer.encode("ascii", "ignore")
model = record.model.encode("ascii", "ignore")
actype = record.actype.encode("ascii", "ignore")
owner = record.owner.encode("ascii", "ignore")
operator = record.operator.encode("ascii", "ignore")
# Add ICAO code with null terminator (original format)
icao24_codes.extend(bytearray(record.icao24 + "\0", encoding="ascii"))
# Add padded data fields (original format)
registration_padding = bytearray(
"\0" * (9 - len(registration)), encoding="ascii"
)
manufacturer_padding = bytearray(
"\0" * (33 - len(manufacturer)), encoding="ascii"
)
model_padding = bytearray("\0" * (33 - len(model)), encoding="ascii")
actype_padding = bytearray("\0" * (5 - len(actype)), encoding="ascii")
owner_padding = bytearray("\0" * (33 - len(owner)), encoding="ascii")
operator_padding = bytearray("\0" * (33 - len(operator)), encoding="ascii")
data.extend(
bytearray(
registration
+ registration_padding
+ manufacturer
+ manufacturer_padding
+ model
+ model_padding
+ actype
+ actype_padding
+ owner
+ owner_padding
+ operator
+ operator_padding
)
)
row_count += 1
with open("icao24.db", "wb") as database:
database.write(icao24_codes + data)
return row_count
def create_database() -> None:
"""Create the ICAO24 database from the downloaded file"""
# Backup existing CSV before downloading new one
has_previous = backup_previous_csv()
# Download new CSV
download_aircraft_database()
# Parse both CSV files for comparison
old_records = {}
if has_previous:
print("Parsing previous CSV file...")
old_records = parse_csv_file("aircraftDatabase_previous.csv")
print(f"Found {len(old_records)} records in previous CSV")
print("Parsing new CSV file...")
new_records = parse_csv_file("aircraftDatabase.csv")
print(f"Found {len(new_records)} records in new CSV")
# Compare records if we have a previous version
if old_records:
new_icao_codes, deleted_icao_codes, changed_icao_codes = compare_records(
old_records, new_records
)
# Print change statistics
print("\n" + "=" * 50)
print("CSV CHANGE SUMMARY")
print("=" * 50)
print(f"New records: {len(new_icao_codes):>8}")
print(f"Deleted records: {len(deleted_icao_codes):>8}")
print(f"Changed records: {len(changed_icao_codes):>8}")
print(f"Total records: {len(new_records):>8}")
print("=" * 50)
# Show examples of changes (limited to avoid spam)
if new_icao_codes and len(new_icao_codes) <= 10:
print(f"\nNew ICAO24 codes: {', '.join(sorted(new_icao_codes))}")
elif new_icao_codes:
sample_new = sorted(list(new_icao_codes))[:5]
print(
f"\nSample new ICAO24 codes: {', '.join(sample_new)} (and {len(new_icao_codes)-5} more)"
)
if deleted_icao_codes and len(deleted_icao_codes) <= 10:
print(f"Deleted ICAO24 codes: {', '.join(sorted(deleted_icao_codes))}")
elif deleted_icao_codes:
sample_deleted = sorted(list(deleted_icao_codes))[:5]
print(
f"Sample deleted ICAO24 codes: {', '.join(sample_deleted)} (and {len(deleted_icao_codes)-5} more)"
)
if changed_icao_codes and len(changed_icao_codes) <= 10:
print(f"Changed ICAO24 codes: {', '.join(sorted(changed_icao_codes))}")
elif changed_icao_codes:
sample_changed = sorted(list(changed_icao_codes))[:5]
print(
f"Sample changed ICAO24 codes: {', '.join(sample_changed)} (and {len(changed_icao_codes)-5} more)"
)
else:
print("\nNo previous CSV found - this appears to be the first run")
# Create database from new records
print("\nCreating ICAO24 database...")
row_count = write_database(new_records)
print("Total of", row_count, "ICAO codes stored in database")
def cleanup_previous_csv() -> None:
"""Remove the previous CSV file if it exists"""
if os.path.exists("aircraftDatabase_previous.csv"):
os.remove("aircraftDatabase_previous.csv")
if __name__ == "__main__":
try:
create_database()
cleanup_previous_csv()
except Exception as e:
print(f"Error creating ICAO24 database: {e}")
else:
print("ICAO24 database created successfully.")
@@ -0,0 +1,130 @@
#!/usr/bin/env python3
# Copyright (C) 2025 Tommaso Ventafridda
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
# -------------------------------------------------------------------------------------
# Create macaddress.db, used for BLE receiver application, using
# https://standards-oui.ieee.org/oui/oui.txt as a source.
# -------------------------------------------------------------------------------------
import urllib.request
import unicodedata
import re
from typing import List, Tuple
def download_oui_file() -> str:
"""Download the OUI file from IEEE"""
url = "https://standards-oui.ieee.org/oui/oui.txt"
print(f"Downloading OUI database from {url}...")
try:
with urllib.request.urlopen(url) as response:
content = response.read().decode("utf-8")
print("Download completed successfully.")
return content
except Exception as e:
print(f"Error downloading OUI file: {e}")
raise
def parse_oui_data(content: str) -> List[Tuple[str, str]]:
"""Parse the OUI file content and extract MAC prefixes and vendor names"""
entries = []
lines = content.split("\n")
for _, line in enumerate(lines):
# Look for lines with (hex) pattern
if "(hex)" in line:
# Extract MAC prefix and vendor name
match = re.match(
r"^([0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2})\s+\(hex\)\s+(.+)$",
line.strip(),
)
if match:
# Remove dashes: "28-6F-B9" -> "286FB9"
mac_prefix = match.group(1).replace(
"-", ""
)
vendor_name = match.group(2).strip()
# Normalize vendor name and limit to 63 characters
vendor_name = (
unicodedata.normalize("NFKD", vendor_name[:63])
.encode("ascii", "ignore")
.decode("ascii")
)
if mac_prefix and vendor_name:
entries.append((mac_prefix, vendor_name))
print(f"Parsed {len(entries)} MAC address entries.")
return entries
def create_database(
entries: List[Tuple[str, str]], output_filename: str = "macaddress.db"
):
"""Create the binary database file."""
# Sort entries by MAC prefix for binary search
entries.sort(key=lambda x: x[0])
mac_prefixes = bytearray()
vendor_data = bytearray()
row_count = 0
for mac_prefix, vendor_name in entries:
# MAC prefix: 6 hex chars + null terminator = 7 bytes
mac_prefixes += bytearray(mac_prefix + "\0", encoding="ascii")
# Vendor name: pad to 64 bytes
vendor_bytes = vendor_name.encode("ascii", "ignore")
vendor_padding = bytearray("\0" * (64 - len(vendor_bytes)), encoding="ascii")
vendor_data += vendor_bytes + vendor_padding
row_count += 1
# Write database: index section followed by data section
with open(output_filename, "wb") as database:
database.write(mac_prefixes + vendor_data)
print(f"Created database '{output_filename}' with {row_count} MAC address entries.")
print(f"Index section: {len(mac_prefixes)} bytes")
print(f"Data section: {len(vendor_data)} bytes")
print(f"Total database size: {len(mac_prefixes) + len(vendor_data)} bytes")
def main():
"""Main function to create the MAC address database."""
try:
oui_content = download_oui_file()
entries = parse_oui_data(oui_content)
if not entries:
print("No valid entries found in OUI file!")
return
create_database(entries)
print("MAC address database creation completed successfully!")
except Exception as e:
print(f"Error creating MAC address database: {e}")
return 1
if __name__ == "__main__":
exit(main())
Binary file not shown.
Binary file not shown.
+12 -34
View File
@@ -1,5 +1,4 @@
# Drone Analog FPV Channels
# https://oscarliang.com/fpv-channels/
# 5.8GHz Analog FPV Channels
f=5865000000,d=FPV Ch A-1
f=5845000000,d=FPV Ch A-2
f=5825000000,d=FPV Ch A-3
@@ -40,30 +39,6 @@ f=5806000000,d=FPV Ch R-5
f=5843000000,d=FPV Ch R-6
f=5880000000,d=FPV Ch R-7
f=5917000000,d=FPV Ch R-8
f=5362000000,d=FPV Ch D-1
f=5399000000,d=FPV Ch D-2
f=5436000000,d=FPV Ch D-3
f=5473000000,d=FPV Ch D-4
f=5510000000,d=FPV Ch D-5
f=5547000000,d=FPV Ch D-6
f=5584000000,d=FPV Ch D-7
f=5621000000,d=FPV Ch D-8
f=5325000000,d=FPV Ch U-1
f=5348000000,d=FPV Ch U-2
f=5366000000,d=FPV Ch U-3
f=5384000000,d=FPV Ch U-4
f=5402000000,d=FPV Ch U-5
f=5420000000,d=FPV Ch U-6
f=5438000000,d=FPV Ch U-7
f=5456000000,d=FPV Ch U-8
f=5474000000,d=FPV Ch O-1
f=5492000000,d=FPV Ch O-2
f=5510000000,d=FPV Ch O-3
f=5528000000,d=FPV Ch O-4
f=5546000000,d=FPV Ch O-5
f=5564000000,d=FPV Ch O-6
f=5582000000,d=FPV Ch O-7
f=5600000000,d=FPV Ch O-8
f=5333000000,d=FPV Ch L-1
f=5373000000,d=FPV Ch L-2
f=5413000000,d=FPV Ch L-3
@@ -72,11 +47,14 @@ f=5493000000,d=FPV Ch L-5
f=5533000000,d=FPV Ch L-6
f=5573000000,d=FPV Ch L-7
f=5613000000,d=FPV Ch L-8
f=5653000000,d=FPV Ch H-1
f=5693000000,d=FPV Ch H-2
f=5733000000,d=FPV Ch H-3
f=5773000000,d=FPV Ch H-4
f=5813000000,d=FPV Ch H-5
f=5853000000,d=FPV Ch H-6
f=5893000000,d=FPV Ch H-7
f=5933000000,d=FPV Ch H-8
# 1.2-1.3GHz Analog Channels
f=1080000000,d=FPV Ch 1
f=1120000000,d=FPV Ch 2
f=1160000000,d=FPV Ch 3
f=1200000000,d=FPV Ch 4
f=1240000000,d=FPV Ch 5
f=1258000000,d=FPV Ch 6
f=1280000000,d=FPV Ch 7
f=1320000000,d=FPV Ch 8
f=1360000000,d=FPV Ch 9
+1 -1
View File
@@ -14,7 +14,7 @@
87,108,FM BROADCAST USA
76,108,FM BROADCAST BRAZIL
76,95,FM BROADCAST JAPAN
65,74,FM BROADCAST RUSSIA
65,74,FM OIRT EAST EU
# CITIZENS BAND RADIO
26,28,CB RADIO
# COMMON PUBLIC SERVICE BANDS
Binary file not shown.