mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2024-12-04 23:45:26 +00:00
Make the CVS Spam app (#2352)
This commit is contained in:
parent
09dff447de
commit
b8a64598ba
313
firmware/application/external/cvs_spam/cvs_spam.cpp
vendored
Normal file
313
firmware/application/external/cvs_spam/cvs_spam.cpp
vendored
Normal file
@ -0,0 +1,313 @@
|
||||
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
|
||||
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
|
||||
// If you can read this, you're a nerd. :P
|
||||
// Come join us at https://discord.gg/thepiratesreborn
|
||||
|
||||
#include "cvs_spam.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "metadata_file.hpp"
|
||||
#include "oversample.hpp"
|
||||
#include "io_convert.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui::external_app::cvs_spam {
|
||||
|
||||
void CVSSpamView::file_error(const std::filesystem::path& path, const std::string& error_details) {
|
||||
nav_.display_modal("Error",
|
||||
"File error:\n" + path.string() + "\n" + error_details);
|
||||
}
|
||||
|
||||
void CVSSpamView::refresh_list() {
|
||||
file_list.clear();
|
||||
current_page = page;
|
||||
uint32_t count = 0;
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(cvsfiles_dir, u"*")) {
|
||||
if (std::filesystem::is_regular_file(entry.status())) {
|
||||
auto filename = entry.path().filename().string();
|
||||
auto extension = entry.path().extension().string();
|
||||
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
|
||||
|
||||
if (extension == ".c16" || extension == ".cu8") {
|
||||
if (count >= (page - 1) * 50 && count < page * 50) {
|
||||
file_list.push_back(entry.path());
|
||||
if (file_list.size() == 50) {
|
||||
page++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file_list.empty()) {
|
||||
if (page == 1) {
|
||||
menu_view.hidden(true);
|
||||
text_empty.hidden(false);
|
||||
} else {
|
||||
page = 1;
|
||||
refresh_list();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
menu_view.hidden(false);
|
||||
text_empty.hidden(true);
|
||||
menu_view.clear();
|
||||
for (const auto& file : file_list) {
|
||||
menu_view.add_item({file.filename().string(),
|
||||
ui::Theme::getInstance()->fg_green->foreground,
|
||||
nullptr,
|
||||
[this](KeyEvent key) {
|
||||
if (key == KeyEvent::Select) {
|
||||
start_tx(menu_view.highlighted_index());
|
||||
}
|
||||
}});
|
||||
}
|
||||
page_info.set("Page " + std::to_string(current_page));
|
||||
menu_view.set_highlighted(0);
|
||||
}
|
||||
|
||||
if (file_list.size() < 50) {
|
||||
page = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CVSSpamView::start_tx(const uint32_t id) {
|
||||
if (is_active()) {
|
||||
stop_tx();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t sample_rate = 500000;
|
||||
|
||||
current_file = cvsfiles_dir / file_list[id].filename();
|
||||
|
||||
File capture_file;
|
||||
auto open_error = capture_file.open(current_file);
|
||||
if (open_error) {
|
||||
file_error(current_file,
|
||||
"Cannot open file.\n"
|
||||
"Initial file check failed.\n"
|
||||
"Path: " +
|
||||
cvsfiles_dir.string() +
|
||||
"\n"
|
||||
"Error: " +
|
||||
std::to_string(static_cast<uint32_t>(open_error)));
|
||||
return;
|
||||
}
|
||||
|
||||
auto metadata_path = get_metadata_path(current_file);
|
||||
auto metadata = read_metadata_file(metadata_path);
|
||||
if (!metadata) {
|
||||
metadata = capture_metadata{transmitter_model.target_frequency(), sample_rate};
|
||||
}
|
||||
|
||||
auto file_size = capture_file.size();
|
||||
capture_file.close();
|
||||
|
||||
replay_thread.reset();
|
||||
transmitter_model.disable();
|
||||
ready_signal = false;
|
||||
|
||||
baseband::set_sample_rate(metadata->sample_rate, get_oversample_rate(metadata->sample_rate));
|
||||
|
||||
auto reader = std::make_unique<FileConvertReader>();
|
||||
if (auto error = reader->open(current_file)) {
|
||||
file_error(current_file,
|
||||
"Cannot read C16 data.\n"
|
||||
"Check file format/perms.\n"
|
||||
"Rate: " +
|
||||
to_string_dec_uint(metadata->sample_rate) +
|
||||
"\n"
|
||||
"Size: " +
|
||||
to_string_dec_uint(file_size) +
|
||||
"\n"
|
||||
"Error: " +
|
||||
std::to_string(static_cast<uint32_t>(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
progressbar.set_value(0);
|
||||
|
||||
transmitter_model.set_sampling_rate(get_actual_sample_rate(metadata->sample_rate));
|
||||
transmitter_model.set_baseband_bandwidth(metadata->sample_rate <= 500000 ? 1750000 : 2500000);
|
||||
transmitter_model.set_target_frequency(metadata->center_frequency);
|
||||
transmitter_model.enable();
|
||||
|
||||
chThdSleepMilliseconds(100);
|
||||
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
read_size,
|
||||
buffer_count,
|
||||
&ready_signal,
|
||||
[](uint32_t return_code) {
|
||||
ReplayThreadDoneMessage message{return_code};
|
||||
EventDispatcher::send_message(message);
|
||||
});
|
||||
}
|
||||
|
||||
void CVSSpamView::start_chaos_tx() {
|
||||
if (is_active()) {
|
||||
stop_tx();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path chaos_file_path = cvsfiles_dir / "chaos.c16";
|
||||
const uint32_t sample_rate = 500000;
|
||||
|
||||
File capture_file;
|
||||
auto open_error = capture_file.open(chaos_file_path);
|
||||
if (open_error) {
|
||||
file_error(chaos_file_path,
|
||||
"Cannot open CHAOS.C16.\n"
|
||||
"Initial file check failed.\n"
|
||||
"Path: " +
|
||||
cvsfiles_dir.string() +
|
||||
"\n"
|
||||
"Error: " +
|
||||
std::to_string(static_cast<uint32_t>(open_error)));
|
||||
return;
|
||||
}
|
||||
|
||||
auto metadata_path = get_metadata_path(chaos_file_path);
|
||||
auto metadata = read_metadata_file(metadata_path);
|
||||
if (!metadata) {
|
||||
metadata = capture_metadata{transmitter_model.target_frequency(), sample_rate};
|
||||
}
|
||||
|
||||
auto file_size = capture_file.size();
|
||||
capture_file.close();
|
||||
|
||||
replay_thread.reset();
|
||||
transmitter_model.disable();
|
||||
ready_signal = false;
|
||||
|
||||
baseband::set_sample_rate(metadata->sample_rate, get_oversample_rate(metadata->sample_rate));
|
||||
|
||||
auto reader = std::make_unique<FileConvertReader>();
|
||||
if (auto error = reader->open(chaos_file_path)) {
|
||||
file_error(chaos_file_path,
|
||||
"Cannot read CHAOS.C16.\n"
|
||||
"Check file format/perms.\n"
|
||||
"Rate: " +
|
||||
to_string_dec_uint(metadata->sample_rate) +
|
||||
"\n"
|
||||
"Size: " +
|
||||
to_string_dec_uint(file_size) +
|
||||
"\n"
|
||||
"Error: " +
|
||||
std::to_string(static_cast<uint32_t>(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
progressbar.set_value(0);
|
||||
|
||||
transmitter_model.set_sampling_rate(get_actual_sample_rate(metadata->sample_rate));
|
||||
transmitter_model.set_baseband_bandwidth(metadata->sample_rate <= 500000 ? 1750000 : 2500000);
|
||||
transmitter_model.set_target_frequency(metadata->center_frequency);
|
||||
transmitter_model.enable();
|
||||
|
||||
chThdSleepMilliseconds(100);
|
||||
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
read_size,
|
||||
buffer_count,
|
||||
&ready_signal,
|
||||
[](uint32_t return_code) {
|
||||
ReplayThreadDoneMessage message{return_code};
|
||||
EventDispatcher::send_message(message);
|
||||
});
|
||||
}
|
||||
|
||||
void CVSSpamView::on_tx_progress(const uint32_t progress) {
|
||||
if (is_active()) {
|
||||
progressbar.set_value(progress % 100);
|
||||
progressbar.set_style(Theme::getInstance()->fg_red);
|
||||
}
|
||||
}
|
||||
|
||||
void CVSSpamView::focus() {
|
||||
menu_view.focus();
|
||||
}
|
||||
|
||||
bool CVSSpamView::is_active() const {
|
||||
return (bool)replay_thread;
|
||||
}
|
||||
|
||||
void CVSSpamView::stop_tx() {
|
||||
replay_thread.reset();
|
||||
transmitter_model.disable();
|
||||
audio::output::stop();
|
||||
ready_signal = false;
|
||||
thread_sync_complete = false;
|
||||
progressbar.set_value(0);
|
||||
chThdSleepMilliseconds(50);
|
||||
}
|
||||
|
||||
CVSSpamView::CVSSpamView(NavigationView& nav)
|
||||
: nav_{nav},
|
||||
current_file{} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_replay);
|
||||
|
||||
add_children({&menu_view,
|
||||
&text_empty,
|
||||
&button_prev_page,
|
||||
&button_next_page,
|
||||
&page_info,
|
||||
&button_send,
|
||||
&button_chaos,
|
||||
&button_stop,
|
||||
&progressbar});
|
||||
|
||||
button_send.set_style(Theme::getInstance()->fg_magenta);
|
||||
button_chaos.set_style(Theme::getInstance()->fg_yellow);
|
||||
button_stop.set_style(Theme::getInstance()->fg_red);
|
||||
|
||||
transmitter_model.set_sampling_rate(1536000);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_tx_gain(47);
|
||||
transmitter_model.set_channel_bandwidth(1750000);
|
||||
|
||||
refresh_list();
|
||||
|
||||
button_prev_page.on_select = [this](Button&) {
|
||||
if (is_active()) return;
|
||||
if (current_page == 1) return;
|
||||
if (current_page == 2) page = 1;
|
||||
page = current_page - 1;
|
||||
refresh_list();
|
||||
};
|
||||
|
||||
button_next_page.on_select = [this](Button&) {
|
||||
if (is_active()) return;
|
||||
refresh_list();
|
||||
};
|
||||
|
||||
button_send.on_select = [this](Button&) {
|
||||
if (!file_list.empty()) {
|
||||
start_tx(menu_view.highlighted_index());
|
||||
}
|
||||
};
|
||||
|
||||
button_chaos.on_select = [this](Button&) {
|
||||
start_chaos_tx();
|
||||
};
|
||||
|
||||
button_stop.on_select = [this](Button&) {
|
||||
if (is_active()) {
|
||||
stop_tx();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
CVSSpamView::~CVSSpamView() {
|
||||
stop_tx();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::cvs_spam
|
115
firmware/application/external/cvs_spam/cvs_spam.hpp
vendored
Normal file
115
firmware/application/external/cvs_spam/cvs_spam.hpp
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
|
||||
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
|
||||
// If you can read this, you're a nerd. :P
|
||||
// Come join us at https://discord.gg/thepiratesreborn
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "replay_thread.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui::external_app::cvs_spam {
|
||||
|
||||
class CVSSpamView : public View {
|
||||
public:
|
||||
explicit CVSSpamView(NavigationView& nav);
|
||||
~CVSSpamView();
|
||||
|
||||
void focus() override;
|
||||
std::string title() const override { return "CVS Spam"; };
|
||||
|
||||
private:
|
||||
static constexpr size_t read_size = 0x4000;
|
||||
static constexpr size_t buffer_count = 3;
|
||||
|
||||
NavigationView& nav_;
|
||||
std::unique_ptr<ReplayThread> replay_thread{};
|
||||
bool ready_signal{false};
|
||||
bool thread_sync_complete{false};
|
||||
std::filesystem::path current_file;
|
||||
|
||||
bool is_active() const;
|
||||
void stop_tx();
|
||||
void file_error(const std::filesystem::path& path, const std::string& error_details);
|
||||
void refresh_list();
|
||||
void start_tx(const uint32_t id);
|
||||
void start_chaos_tx();
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
|
||||
uint32_t page = 1;
|
||||
uint32_t current_page = 1;
|
||||
std::vector<std::filesystem::path> file_list{};
|
||||
|
||||
MenuView menu_view{
|
||||
{0, 0, 240, 180},
|
||||
true};
|
||||
|
||||
Text text_empty{
|
||||
{7 * 8, 12 * 8, 16 * 8, 16},
|
||||
"Empty directory!"};
|
||||
|
||||
Button button_prev_page{
|
||||
{0, 180, 50, 32},
|
||||
"<"};
|
||||
|
||||
Button button_next_page{
|
||||
{190, 180, 50, 32},
|
||||
">"};
|
||||
|
||||
Text page_info{
|
||||
{95, 188, 50, 16}};
|
||||
|
||||
Button button_send{
|
||||
{0, 212, 75, 36},
|
||||
"CALL"};
|
||||
|
||||
Button button_chaos{
|
||||
{82, 212, 75, 36},
|
||||
"CHAOS"};
|
||||
|
||||
Button button_stop{
|
||||
{165, 212, 75, 36},
|
||||
LanguageHelper::currentMessages[LANG_STOP]};
|
||||
|
||||
ProgressBar progressbar{
|
||||
{0, 256, 240, 44}};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal{
|
||||
Message::ID::RequestSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const RequestSignalMessage*>(p);
|
||||
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
|
||||
ready_signal = true;
|
||||
}
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_replay_thread_done{
|
||||
Message::ID::ReplayThreadDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
if (message.return_code == ReplayThread::END_OF_FILE) {
|
||||
if (is_active()) {
|
||||
thread_sync_complete = true;
|
||||
stop_tx();
|
||||
}
|
||||
} else if (message.return_code == ReplayThread::READ_ERROR) {
|
||||
file_error(file_list[menu_view.highlighted_index()], "Read error during playback");
|
||||
}
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::cvs_spam
|
33
firmware/application/external/cvs_spam/main.cpp
vendored
Normal file
33
firmware/application/external/cvs_spam/main.cpp
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
|
||||
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
|
||||
// If you can read this, you're a nerd. :P
|
||||
// Come join us at https://discord.gg/thepiratesreborn
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "cvs_spam.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::cvs_spam {
|
||||
void initialize_app(NavigationView& nav) {
|
||||
nav.push<CVSSpamView>();
|
||||
}
|
||||
} // namespace ui::external_app::cvs_spam
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_cvs_spam.application_information"), used)) application_information_t _application_information_cvs_spam = {
|
||||
(uint8_t*)0x00000000,
|
||||
ui::external_app::cvs_spam::initialize_app,
|
||||
CURRENT_HEADER_VERSION,
|
||||
VERSION_MD5,
|
||||
"CVS Spam",
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81},
|
||||
ui::Color::red().v,
|
||||
app_location_t::TX,
|
||||
{'P', 'R', 'E', 'P'},
|
||||
0x00000000};
|
||||
}
|
5
firmware/application/external/external.cmake
vendored
5
firmware/application/external/external.cmake
vendored
@ -110,6 +110,10 @@ set(EXTCPPSRC
|
||||
#shoppingcart_lock
|
||||
external/shoppingcart_lock/main.cpp
|
||||
external/shoppingcart_lock/shoppingcart_lock.cpp
|
||||
|
||||
#cvs_spam
|
||||
external/cvs_spam/main.cpp
|
||||
external/cvs_spam/cvs_spam.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@ -131,6 +135,7 @@ set(EXTAPPLIST
|
||||
foxhunt_rx
|
||||
audio_test
|
||||
wardrivemap
|
||||
cvs_spam
|
||||
tpmsrx
|
||||
protoview
|
||||
adsbtx
|
||||
|
7
firmware/application/external/external.ld
vendored
7
firmware/application/external/external.ld
vendored
@ -49,6 +49,7 @@ MEMORY
|
||||
ram_external_app_random_password(rwx) : org = 0xADC80000, len = 32k
|
||||
ram_external_app_acars_rx(rwx) : org = 0xADC90000, len = 32k
|
||||
ram_external_app_shoppingcart_lock(rwx) : org = 0xADCA0000, len = 32k
|
||||
ram_external_app_cvs_spam(rwx) : org = 0xADCB0000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@ -210,6 +211,10 @@ SECTIONS
|
||||
*(*ui*external_app*shoppingcart_lock*);
|
||||
} > ram_external_app_shoppingcart_lock
|
||||
|
||||
|
||||
.external_app_cvs_spam : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_cvs_spam.application_information));
|
||||
*(*ui*external_app*cvs_spam*);
|
||||
} > ram_external_app_cvs_spam
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ const std::filesystem::path audio_dir = u"AUDIO";
|
||||
const std::filesystem::path blerx_dir = u"BLERX";
|
||||
const std::filesystem::path bletx_dir = u"BLETX";
|
||||
const std::filesystem::path captures_dir = u"CAPTURES";
|
||||
const std::filesystem::path cvsfiles_dir = u"CVSFILES";
|
||||
const std::filesystem::path debug_dir = u"DEBUG";
|
||||
const std::filesystem::path firmware_dir = u"FIRMWARE";
|
||||
const std::filesystem::path freqman_dir = u"FREQMAN";
|
||||
|
@ -32,6 +32,7 @@ extern const std::filesystem::path audio_dir;
|
||||
extern const std::filesystem::path blerx_dir;
|
||||
extern const std::filesystem::path bletx_dir;
|
||||
extern const std::filesystem::path captures_dir;
|
||||
extern const std::filesystem::path cvsfiles_dir;
|
||||
extern const std::filesystem::path debug_dir;
|
||||
extern const std::filesystem::path firmware_dir;
|
||||
extern const std::filesystem::path freqman_dir;
|
||||
|
Loading…
Reference in New Issue
Block a user