You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
6.5 KiB
191 lines
6.5 KiB
#include <monoformat_fontreader.hpp>
|
|
#include <monoformat_structured.hpp>
|
|
#include <monoformat_parseonly.hpp>
|
|
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <stdexcept>
|
|
#include <atomic>
|
|
#include <csignal>
|
|
#include <thread>
|
|
#include <chrono>
|
|
|
|
using namespace monoformat;
|
|
|
|
constexpr static std::uint16_t const ScreenWidth = 120;
|
|
constexpr static std::uint16_t const ScreenHeight = 60;
|
|
|
|
static std::atomic<bool> g_ctrlCPressed;
|
|
|
|
static void handleCtrlC(int) {
|
|
g_ctrlCPressed.store(true, std::memory_order_relaxed);
|
|
}
|
|
|
|
template<typename Container>
|
|
static Container readEntireFile(std::filesystem::path const& fileName) {
|
|
std::ifstream fileReader(fileName, std::ios::binary | std::ios::ate | std::ios::in);
|
|
if (!fileReader) {
|
|
std::ostringstream buf;
|
|
buf << "Could not open \"" << fileName << "\" for reading";
|
|
throw std::runtime_error(buf.str());
|
|
}
|
|
auto fileSize = fileReader.tellg();
|
|
fileReader.seekg(std::ios::beg);
|
|
typename Container::size_type scalarSize = sizeof(typename Container::value_type);
|
|
Container content((static_cast<typename Container::size_type>(fileSize) + scalarSize - 1) / scalarSize,
|
|
static_cast<typename Container::value_type>(0));
|
|
fileReader.read(reinterpret_cast<char*>(&content[0]), static_cast<std::size_t>(fileSize));
|
|
return content;
|
|
}
|
|
|
|
static void clearScreen() {
|
|
std::cout << "\x1b[1J\x1b[1;1H" << std::flush;
|
|
}
|
|
|
|
static void renderScreeen(monoformat::OneBitBufferInterface const& imageBuffer) {
|
|
std::uint16_t const w = ScreenWidth;
|
|
std::uint16_t const h = ScreenHeight / 2;
|
|
|
|
std::ostringstream buf;
|
|
buf << "\xE2\x94\x8C";
|
|
for (std::uint16_t i = 0; i < w; ++i) {
|
|
buf << "\xE2\x94\x80";
|
|
}
|
|
buf << "\xE2\x94\x90\n";
|
|
for (std::uint16_t y = 0; y < h; ++y) {
|
|
buf << "\xE2\x94\x82";
|
|
for (std::uint16_t x = 0; x < w; ++x) {
|
|
bool const pxUpperSet = imageBuffer.isPixelSet(x, 2 * y + 0);
|
|
bool const pxLowerSet = imageBuffer.isPixelSet(x, 2 * y + 1);
|
|
if (pxUpperSet && pxLowerSet) {
|
|
buf << "\xE2\x96\x88";
|
|
} else if (pxUpperSet) {
|
|
buf << "\xF0\x9F\xAC\x8E";
|
|
} else if (pxLowerSet) {
|
|
buf << "\xE2\x96\x84";
|
|
} else {
|
|
buf << " ";
|
|
}
|
|
}
|
|
buf << "\xE2\x94\x82\n";
|
|
}
|
|
if (ScreenHeight % 1) {
|
|
buf << "\xE2\x94\x82";
|
|
for (std::uint16_t x = 0; x < w; ++x) {
|
|
bool const pxUpperSet = imageBuffer.isPixelSet(x, ScreenHeight - 1);
|
|
if (pxUpperSet) {
|
|
buf << "\xF0\x9F\xAC\x8E";
|
|
} else {
|
|
buf << " ";
|
|
}
|
|
}
|
|
buf << "\xE2\x94\x82\n";
|
|
}
|
|
buf << "\xE2\x94\x94";
|
|
for (std::uint16_t i = 0; i < w; ++i) {
|
|
buf << "\xE2\x94\x80";
|
|
}
|
|
buf << "\xE2\x94\x98\n";
|
|
std::cout << buf.str();
|
|
}
|
|
|
|
static void showContents(monoformat::File const& file, std::size_t tick, std::int64_t timestamp, std::span<monoformat::CustomFontSection const*> customFonts) {
|
|
monoformat::ImageElement imageBufferElement{0, 0, ScreenWidth, ScreenHeight};
|
|
auto imageBuffer = imageBufferElement.image();
|
|
|
|
for (auto const& section : file.sections) {
|
|
if (auto sec = dynamic_cast<monoformat::AlwaysDrawnSection*>(section.get())) {
|
|
for (std::size_t i = 0; i < sec->elementCount(); ++i) {
|
|
sec->elementAt(i)->drawTo(&imageBuffer, tick, timestamp, customFonts);
|
|
}
|
|
} else if (auto sec = dynamic_cast<monoformat::TimeBasedDrawnSection*>(section.get())) {
|
|
for (std::size_t i = 0; i < sec->elementCount(); ++i) {
|
|
sec->elementAt(i)->drawTo(&imageBuffer, tick, timestamp, customFonts);
|
|
}
|
|
}
|
|
}
|
|
|
|
renderScreeen(imageBuffer);
|
|
}
|
|
|
|
static void showContents2(std::span<std::byte const> data, std::size_t tick, std::int64_t timestamp) {
|
|
std::vector<std::byte> buffer;
|
|
buffer.resize((ScreenWidth * ScreenHeight + 8 - 1) / 8);
|
|
MemoryOneBitBuffer imageBuffer{buffer, ScreenWidth, ScreenHeight};
|
|
|
|
auto ret = parseAndApply(data, &imageBuffer, ScreenWidth, ScreenHeight, true, tick, timestamp);
|
|
if (!ret) {
|
|
std::cerr << "Parse & apply error\n";
|
|
}
|
|
|
|
renderScreeen(imageBuffer);
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc < 2 || !argv[1]) {
|
|
std::ostringstream buf;
|
|
buf << "Usage: " << argv[0] << " filename [timestamp]\n";
|
|
std::cerr << buf.str();
|
|
return 1;
|
|
}
|
|
if (std::string_view{argv[1]} == "--help") {
|
|
std::ostringstream buf;
|
|
buf << "Usage: " << argv[0] << " filename [timestamp]\n";
|
|
std::cout << buf.str() << std::flush;
|
|
return 0;
|
|
}
|
|
|
|
std::int64_t timestamp = -1;
|
|
if (argc > 2) {
|
|
char* endptr = nullptr;
|
|
timestamp = strtoll(argv[2], &endptr, 10);
|
|
if (!endptr || *endptr) {
|
|
std::ostringstream buf;
|
|
buf << "Usage: " << argv[0] << " filename [timestamp]\n";
|
|
std::cerr << buf.str();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
try {
|
|
auto contents = readEntireFile<std::vector<std::byte>>(argv[1]);
|
|
auto file = monoformat::parseFile(contents);
|
|
if (!file) {
|
|
std::ostringstream buf;
|
|
buf << "Parse error while processing the file \"" << argv[1] << "\"";
|
|
throw std::runtime_error(buf.str());
|
|
}
|
|
|
|
std::vector<monoformat::CustomFontSection const*> customFonts;
|
|
for (auto const& section : file->sections) {
|
|
if (auto fs = dynamic_cast<monoformat::CustomFontSection const*>(section.get())) {
|
|
customFonts.push_back(fs);
|
|
}
|
|
}
|
|
|
|
std::signal(SIGINT, &handleCtrlC);
|
|
std::signal(SIGTERM, &handleCtrlC);
|
|
std::size_t tick = 0;
|
|
while (!g_ctrlCPressed.load(std::memory_order_relaxed)) {
|
|
std::int64_t currentTime = timestamp == -1 ? time(nullptr) : timestamp;
|
|
clearScreen();
|
|
//showContents(*file, tick, currentTime, customFonts);
|
|
showContents2(contents, tick, currentTime);
|
|
std::ostringstream buf;
|
|
buf << "Animation Tick: " << tick << ", Timestamp = " << currentTime << "\n";
|
|
std::cout << buf.str() << std::flush;
|
|
std::this_thread::sleep_for(std::chrono::milliseconds{40});
|
|
++tick;
|
|
}
|
|
} catch (std::exception& e) {
|
|
std::ostringstream buf;
|
|
buf << argv[0] << ": " << e.what() << "\n";
|
|
std::cerr << buf.str();
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|