From 23f5a4386e2226a4c90d429e850ae24411500220 Mon Sep 17 00:00:00 2001 From: Slendi Date: Mon, 8 Sep 2025 05:32:43 +0300 Subject: [PATCH] update Signed-off-by: Slendi --- bfl.cpp | 747 ++++++++++++++++++++++++-------------------------------- 1 file changed, 314 insertions(+), 433 deletions(-) diff --git a/bfl.cpp b/bfl.cpp index 34ff7e1..c155305 100644 --- a/bfl.cpp +++ b/bfl.cpp @@ -1,73 +1,58 @@ #include +#include #include #include #include #include #include +#include #include #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" - #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" -// Helpers - -inline void store_le16(std::vector &buf, uint16_t val) { - buf.push_back(static_cast(val & 0xFF)); - buf.push_back(static_cast((val >> 8) & 0xFF)); +template inline void put_le(std::vector &buf, T v) { + static_assert(std::is_unsigned_v); + for (size_t i{}; i < sizeof(T); i++) + buf.push_back(static_cast((v >> (i * 8)) & 0xFF)); +} +template inline T get_le(std::span s, size_t off) { + static_assert(std::is_unsigned_v); + T v = 0; + for (size_t i{}; i < sizeof(T); i++) + v |= (static_cast(s[off + i]) << (i * 8)); + return v; } -inline void store_le32(std::vector &buf, uint32_t val) { - buf.push_back(static_cast(val & 0xFF)); - buf.push_back(static_cast((val >> 8) & 0xFF)); - buf.push_back(static_cast((val >> 16) & 0xFF)); - buf.push_back(static_cast((val >> 24) & 0xFF)); +inline std::vector read_file(std::filesystem::path const &p) { + std::ifstream f(p, std::ios::binary); + return std::vector((std::istreambuf_iterator(f)), + std::istreambuf_iterator()); +} +inline void write_file(std::filesystem::path const &p, + std::span d) { + std::ofstream f(p, std::ios::binary); + f.write(reinterpret_cast(d.data()), + static_cast(d.size())); } -inline void store_le64(std::vector &buf, uint64_t val) { - buf.push_back(static_cast(val & 0xFF)); - buf.push_back(static_cast((val >> 8) & 0xFF)); - buf.push_back(static_cast((val >> 16) & 0xFF)); - buf.push_back(static_cast((val >> 24) & 0xFF)); - buf.push_back(static_cast((val >> 32) & 0xFF)); - buf.push_back(static_cast((val >> 40) & 0xFF)); - buf.push_back(static_cast((val >> 48) & 0xFF)); - buf.push_back(static_cast((val >> 56) & 0xFF)); -} - -static inline uint16_t read_le16(std::span s, size_t off) { - return static_cast(s[off] | (s[off + 1] << 8)); -} - -static inline uint32_t read_le32(std::span s, size_t off) { - return static_cast(s[off] | (s[off + 1] << 8) | (s[off + 2] << 16) | - (s[off + 3] << 24)); -} - -static std::vector lzss_compress(std::span in) { - const int W = 4096; - const int LA = 18; - const int MIN = 3; - +static std::vector lzss_compress(std::span in) { + int const W{4096}, LA{18}, MIN{3}; std::vector out; - size_t i = 0; - + out.reserve(in.size() / 8 + 16); + size_t i{}; while (i < in.size()) { - uint8_t flag = 0; - size_t flag_pos = out.size(); + uint8_t flag{}; + size_t flag_pos{out.size()}; out.push_back(0); - - for (int bit = 0; bit < 8 && i < in.size(); ++bit) { - size_t best_len = 0; - size_t best_off = 0; - - size_t wnd_start = (i > (size_t)W) ? i - W : 0; - size_t max_len = std::min((size_t)LA, in.size() - i); - - for (size_t p = i; p-- > wnd_start;) { - size_t l = 0; + for (int bit{}; bit < 8 && i < in.size(); bit++) { + size_t best_len{}, best_off{}; + size_t wnd_start{(i > (size_t)W) ? i - W : 0}; + size_t max_len{std::min((size_t)LA, in.size() - i)}; + for (size_t p{i}; p-- > wnd_start;) { + size_t l{}; while (l < max_len && in[p + l] == in[i + l]) l++; if (l >= (size_t)MIN && l > best_len) { @@ -77,157 +62,188 @@ static std::vector lzss_compress(std::span in) { break; } } - if (best_len >= (size_t)MIN && best_off >= 1 && best_off <= 4095) { uint8_t b0 = static_cast(((best_len - 3) << 4) | (best_off >> 8)); - uint8_t b1 = static_cast(best_off & 0xFF); + uint8_t b1{static_cast(best_off & 0xFF)}; out.push_back(b0); out.push_back(b1); i += best_len; } else { flag |= (1u << bit); - out.push_back(in[i]); - ++i; + out.push_back(in[i++]); } } - out[flag_pos] = flag; } - return out; } - -// "Library" - -constexpr uint8_t FLAG_HAS_ALPHA = 0x01; -constexpr uint8_t FLAG_IMG_RAW = 0x02; // pre-LZSS image was RAW bitpack -constexpr uint8_t FLAG_TRA_RAW = 0x04; // pre-LZSS mask was RAW bitpack -constexpr uint8_t FLAG_IMG_NOLZ = 0x08; // image stream stored without LZSS -constexpr uint8_t FLAG_TRA_NOLZ = 0x10; // mask stream stored without LZSS - -static std::vector -rle_encode_with_transparency(const std::vector &data, bool initial, - const std::vector *transparency, - bool allow_trick) { +static std::vector lzss_decompress(std::span in) { std::vector out; - out.push_back(static_cast(initial)); - - bool state = initial; - size_t count = 0; - size_t i = 0; - - auto flush_emit = [&](uint8_t n) { out.push_back(n); }; - - while (i < data.size()) { - bool is_transparent = transparency && (*transparency)[i]; - - if (allow_trick && is_transparent) { - while (i < data.size() && (*transparency)[i]) { - if (count == 255) { - flush_emit(255); - state = !state; - count = 0; - } - ++count; - ++i; - } - continue; - } - - bool bit = data[i]; - if (bit == state) { - ++count; - ++i; - - if (count == 255 && i < data.size()) { - flush_emit(255); - out.push_back(0); - count = 0; - } - } else { - flush_emit(static_cast(count)); - state = !state; - count = 1; - ++i; - } - } - - if (count > 0) - flush_emit(static_cast(count)); - return out; -} - -static std::vector lzss_decompress(std::span in) { - std::vector out; - size_t i = 0; - + out.reserve(in.size() * 2); + size_t i{}; while (i < in.size()) { - uint8_t flag = in[i++]; - for (int bit = 0; bit < 8 && i < in.size(); ++bit) { - bool is_lit = (flag >> bit) & 1; - if (is_lit) { + uint8_t flag{in[i++]}; + for (int bit{}; bit < 8 && i < in.size(); bit++) { + if ((flag >> bit) & 1) { out.push_back(in[i++]); } else { if (i + 1 >= in.size()) return out; - uint8_t b0 = in[i++]; - uint8_t b1 = in[i++]; - size_t len = (b0 >> 4) + 3; - size_t off = ((b0 & 0x0F) << 8) | b1; // 1..4095 - + uint8_t b0{in[i++]}, b1{in[i++]}; + size_t len{static_cast((b0 >> 4) + 3)}; + size_t off{static_cast(((b0 & 0x0F) << 8) | b1)}; if (off == 0 || off > out.size()) - return out; // invalid - size_t src = out.size() - off; - for (size_t k = 0; k < len; ++k) { + return out; + size_t src{out.size() - off}; + for (size_t k{}; k < len; k++) out.push_back(out[src + k]); - } } } } return out; } -static std::vector rle_decode(std::span in, +enum : uint8_t { + FLAG_HAS_ALPHA = 0x01, + FLAG_IMG_RAW = 0x02, + FLAG_TRA_RAW = 0x04, + FLAG_IMG_NOLZ = 0x08, + FLAG_TRA_NOLZ = 0x10, +}; + +static std::vector bits_to_bytes(std::vector const &data) { + std::vector bytes((data.size() + 7) / 8, 0); + for (size_t i{}; i < data.size(); i++) + if (data[i]) + bytes[i / 8] |= (1u << (i % 8)); + return bytes; +} + +static std::vector decode_raw(std::span in, + size_t total_bits) { + std::vector out(total_bits, false); + for (size_t i{}; i < total_bits; i++) { + uint8_t byte = (i / 8 < in.size()) ? in[i / 8] : 0; + out[i] = ((byte >> (i % 8)) & 1) != 0; + } + return out; +} + +static std::vector +rle_encode_with_transparency(std::vector const &data, bool initial, + std::vector const *transparency, + bool allow_trick) { + std::vector out; + out.reserve(data.size() / 4 + 8); + out.push_back(static_cast(initial)); + + bool state{initial}; + size_t count{}, i{}; + + auto flush{[&](uint8_t n) { out.push_back(n); }}; + + while (i < data.size()) { + bool is_transparent{transparency && (*transparency)[i]}; + + if (allow_trick && is_transparent) { + while (i < data.size() && (*transparency)[i]) { + if (count == 255) { + flush(255); + state = !state; + count = 0; + } + count++; + i++; + } + continue; + } + + bool bit{data[i]}; + if (bit == state) { + count++; + i++; + if (count == 255 && i < data.size()) { + flush(255); + out.push_back(0); + count = 0; + } + } else { + flush(static_cast(count)); + state = !state; + count = 1; + i++; + } + } + if (count) + flush(static_cast(count)); + return out; +} + +static std::vector rle_decode(std::span in, size_t total_bits) { std::vector out(total_bits, false); if (in.size() < 2) return out; - bool state = in[0] != 0; - size_t produced = 0; - size_t j = 1; + bool state{in[0] != 0}; + size_t produced{0}, j{1}; while (produced < total_bits && j < in.size()) { - uint8_t n = in[j++]; - - if (n == 0) { + uint8_t n{in[j++]}; + if (n == 0) continue; - } - - size_t emit = std::min(n, total_bits - produced); - for (size_t k = 0; k < emit; ++k) + size_t emit{std::min(n, total_bits - produced)}; + for (size_t k{}; k < emit; k++) out[produced + k] = state; produced += emit; - - if (j < in.size() && in[j] == 0) { - ++j; - } else { + if (j < in.size() && in[j] == 0) + j++; + else state = !state; - } } - return out; } -static std::vector raw_decode(std::span in, - size_t total_bits) { - std::vector out(total_bits, false); - for (size_t i = 0; i < total_bits; ++i) { - uint8_t byte = (i / 8 < in.size()) ? in[i / 8] : 0; - out[i] = ((byte >> (i % 8)) & 1) != 0; - } - return out; +struct BFLHeader { + uint16_t w{}, h{}; + uint8_t flags{}; + uint32_t img_len{}, tra_len{}; +}; + +struct BFLView { + BFLHeader hdr{}; + std::span img_c{}; + std::span tra_c{}; +}; + +static std::optional parse_bfl(std::span data) { + if (data.size() < 3 + 2 + 2 + 1 + 4 + 4) + return std::nullopt; + if (!(data[0] == 'B' && data[1] == 'F' && data[2] == 'L')) + return std::nullopt; + + size_t off{3}; + BFLView v; + v.hdr.w = get_le(data, off); + off += 2; + v.hdr.h = get_le(data, off); + off += 2; + v.hdr.flags = data[off++]; + v.hdr.img_len = get_le(data, off); + off += 4; + v.hdr.tra_len = get_le(data, off); + off += 4; + + if (off + v.hdr.img_len > data.size()) + return std::nullopt; + if (off + v.hdr.img_len + v.hdr.tra_len > data.size()) + return std::nullopt; + + v.img_c = data.subspan(off, v.hdr.img_len); + off += v.hdr.img_len; + v.tra_c = data.subspan(off, v.hdr.tra_len); + return v; } struct Bitmap { @@ -235,261 +251,149 @@ struct Bitmap { std::vector image_data; std::optional> transparency_data; - auto to_rgba() -> std::vector; - auto encode() -> std::vector { - auto const raw_encode = - [](std::vector const &data) -> std::vector { - std::vector bytes((data.size() + 7) / 8, 0); - for (size_t i = 0; i < data.size(); ++i) { - if (data[i]) - bytes[i / 8] |= (1u << (i % 8)); - } - return bytes; - }; + std::vector to_rgba() const { + auto mk{[](uint8_t r, uint8_t g, uint8_t b, uint8_t a) -> uint32_t { + return (uint32_t)r | ((uint32_t)g << 8) | ((uint32_t)b << 16) | + ((uint32_t)a << 24); + }}; + size_t n{(size_t)width * (size_t)height}; + std::vector out(n, 0); + for (size_t i{}; i < n; i++) { + bool tr{transparency_data && i < transparency_data->size() && + (*transparency_data)[i]}; + bool wh{i < image_data.size() && image_data[i]}; + out[i] = tr ? mk(0, 0, 0, 0) + : (wh ? mk(255, 255, 255, 255) : mk(0, 0, 0, 255)); + } + return out; + } - std::vector ret; - - // header - ret.push_back('B'); - ret.push_back('F'); - ret.push_back('L'); - store_le16(ret, width); - store_le16(ret, height); - - auto encode_best = [&](std::vector const &data, bool &raw_flag, - bool allow_trick) { - const std::vector *mask = - transparency_data ? &(*transparency_data) : nullptr; - - auto init_false = - rle_encode_with_transparency(data, false, mask, allow_trick); - auto init_true = - rle_encode_with_transparency(data, true, mask, allow_trick); - - std::vector result = (init_false.size() <= init_true.size()) - ? std::move(init_false) - : std::move(init_true); - - if (result.size() * 8 > data.size()) { - result = raw_encode(data); + std::vector encode() const { + auto choose_rle_or_raw{[&](std::vector const &bits, bool &raw_flag, + bool allow_trick) { + std::vector const *mask{transparency_data ? &(*transparency_data) + : nullptr}; + auto a{rle_encode_with_transparency(bits, false, mask, allow_trick)}; + auto b{rle_encode_with_transparency(bits, true, mask, allow_trick)}; + std::vector best{(a.size() <= b.size()) ? std::move(a) + : std::move(b)}; + if (best.size() * 8 > bits.size()) { + best = bits_to_bytes(bits); raw_flag = true; } - return result; - }; + return best; + }}; - std::vector image_bytes, trans_bytes; - bool image_raw = false, trans_raw = false; + std::vector out; + out.push_back('B'); + out.push_back('F'); + out.push_back('L'); + put_le(out, width); + put_le(out, height); - image_bytes = - encode_best(image_data, image_raw, transparency_data.has_value()); - if (transparency_data) { - trans_bytes = encode_best(*transparency_data, trans_raw, false); - } - - bool img_no_lz = false, tra_no_lz = false; - - std::vector img_stream_lz = lzss_compress(image_bytes); - std::vector img_stream = - (img_stream_lz.size() < image_bytes.size()) - ? std::move(img_stream_lz) - : (img_no_lz = true, image_bytes); + bool img_raw{}, tra_raw{}; + auto img_bytes = + choose_rle_or_raw(image_data, img_raw, transparency_data.has_value()); + std::vector tra_bytes; + if (transparency_data) + tra_bytes = choose_rle_or_raw(*transparency_data, tra_raw, false); + auto maybe_lz{[](std::vector v, bool &no_lz) { + auto c{lzss_compress(v)}; + if (c.size() < v.size()) + return c; + no_lz = true; + return v; + }}; + bool img_no_lz{}, tra_no_lz{}; + auto img_stream{maybe_lz(std::move(img_bytes), img_no_lz)}; std::vector tra_stream; - if (!trans_bytes.empty()) { - auto tra_stream_lz = lzss_compress(trans_bytes); - tra_stream = (tra_stream_lz.size() < trans_bytes.size()) - ? std::move(tra_stream_lz) - : (tra_no_lz = true, trans_bytes); - } + if (!tra_bytes.empty()) + tra_stream = maybe_lz(std::move(tra_bytes), tra_no_lz); - // flags - uint8_t flags = 0; + uint8_t flags{}; if (transparency_data) flags |= FLAG_HAS_ALPHA; - if (image_raw) + if (img_raw) flags |= FLAG_IMG_RAW; - if (trans_raw) + if (tra_raw) flags |= FLAG_TRA_RAW; if (img_no_lz) flags |= FLAG_IMG_NOLZ; if (tra_no_lz) flags |= FLAG_TRA_NOLZ; - ret.push_back(flags); - store_le32(ret, static_cast(img_stream.size())); - store_le32(ret, static_cast(tra_stream.size())); - - // payload - ret.insert(ret.end(), img_stream.begin(), img_stream.end()); - ret.insert(ret.end(), tra_stream.begin(), tra_stream.end()); - - return ret; + out.push_back(flags); + put_le(out, static_cast(img_stream.size())); + put_le(out, static_cast(tra_stream.size())); + out.insert(out.end(), img_stream.begin(), img_stream.end()); + out.insert(out.end(), tra_stream.begin(), tra_stream.end()); + return out; } - static auto from_rgba(std::span const &data, int width, - int height) -> Bitmap; - static auto decode(std::span const &data) -> Bitmap; -}; + static Bitmap from_rgba(std::span data, int w, int h) { + assert(w > 0 && h > 0); + assert(static_cast(data.size()) == w * h); -auto Bitmap::decode(std::span const &data) -> Bitmap { - Bitmap bm{}; + Bitmap bm; + bm.width = (uint16_t)w; + bm.height = (uint16_t)h; + size_t n{(size_t)w * (size_t)h}; + bm.image_data.resize(n); + bm.transparency_data.emplace(); + bm.transparency_data->resize(n); - if (data.size() < 3 + 2 + 2 + 1 + 4 + 4) - return bm; - if (!(data[0] == 'B' && data[1] == 'F' && data[2] == 'L')) - return bm; - - size_t off = 3; - uint16_t w = read_le16(data, off); - off += 2; - uint16_t h = read_le16(data, off); - off += 2; - - uint8_t flags = data[off++]; - - uint32_t img_c_len = read_le32(data, off); - off += 4; - uint32_t tra_c_len = read_le32(data, off); - off += 4; - - if (off + img_c_len > data.size()) - return bm; - if (off + img_c_len + tra_c_len > data.size()) - return bm; - - auto img_c = data.subspan(off, img_c_len); - off += img_c_len; - auto tra_c = data.subspan(off, tra_c_len); - - bool has_alpha = (flags & FLAG_HAS_ALPHA) != 0; - bool img_was_raw = (flags & FLAG_IMG_RAW) != 0; - bool tra_was_raw = (flags & FLAG_TRA_RAW) != 0; - bool img_no_lz = (flags & FLAG_IMG_NOLZ) != 0; - bool tra_no_lz = (flags & FLAG_TRA_NOLZ) != 0; - - std::vector img_stream = - img_no_lz ? std::vector(img_c.begin(), img_c.end()) - : lzss_decompress(img_c); - - std::vector tra_stream = - (has_alpha ? (tra_no_lz ? std::vector(tra_c.begin(), tra_c.end()) - : lzss_decompress(tra_c)) - : std::vector{}); - - size_t total_bits = static_cast(w) * static_cast(h); - - std::vector img_bits = img_was_raw ? raw_decode(img_stream, total_bits) - : rle_decode(img_stream, total_bits); - - std::optional> tra_bits; - if (has_alpha) { - tra_bits = (tra_was_raw ? raw_decode(tra_stream, total_bits) - : rle_decode(tra_stream, total_bits)); - } - - bm.width = w; - bm.height = h; - bm.image_data = std::move(img_bits); - bm.transparency_data = std::move(tra_bits); - return bm; -} - -auto Bitmap::from_rgba(std::span const &data, int w, int h) - -> Bitmap { - assert(w > 0 && h > 0); - assert(static_cast(data.size()) == w * h); - - Bitmap bm; - bm.width = w; - bm.height = h; - bm.image_data.resize(static_cast(w) * static_cast(h)); - bm.transparency_data = std::vector{}; - bm.transparency_data->resize(static_cast(w) * static_cast(h)); - - for (size_t i = 0; i < data.size(); ++i) { - uint32_t px = data[i]; - - // little-endian: 0xAABBGGRR - uint8_t r = static_cast((px >> 0) & 0xFF); - uint8_t g = static_cast((px >> 8) & 0xFF); - uint8_t b = static_cast((px >> 16) & 0xFF); - uint8_t a = static_cast((px >> 24) & 0xFF); - - bool is_white = (r == 255 && g == 255 && b == 255 && a != 0); - bool is_black = (r == 0 && g == 0 && b == 0 && a != 0); - - bool is_transparent = (!is_white && !is_black) || (a == 0); - - (*bm.transparency_data)[i] = is_transparent; - bm.image_data[i] = is_white; - } - - return bm; -} - -auto Bitmap::to_rgba() -> std::vector { - std::vector out; - out.resize(static_cast(width) * static_cast(height)); - - auto make_rgba = [](uint8_t r, uint8_t g, uint8_t b, uint8_t a) -> uint32_t { - return (static_cast(r) << 0) | (static_cast(g) << 8) | - (static_cast(b) << 16) | (static_cast(a) << 24); - }; - - for (size_t i = 0; i < out.size(); ++i) { - if ((transparency_data && i >= transparency_data->size()) || - i >= image_data.size()) { - out[i] = make_rgba(0, 0, 0, 0); // safety - continue; - } - - if (transparency_data && (*transparency_data)[i]) { - out[i] = make_rgba(0, 0, 0, 0); - } else { - if (image_data[i]) { - out[i] = make_rgba(255, 255, 255, 255); - } else { - out[i] = make_rgba(0, 0, 0, 255); - } + for (size_t i{}; i < n; i++) { + uint32_t px{data[i]}; + uint8_t r = (px >> 0) & 0xFF, g = (px >> 8) & 0xFF, b = (px >> 16) & 0xFF, + a = (px >> 24) & 0xFF; + bool white{(r == 255 && g == 255 && b == 255 && a != 0)}; + bool black{(r == 0 && g == 0 && b == 0 && a != 0)}; + bool tr{(!white && !black) || (a == 0)}; + (*bm.transparency_data)[i] = tr; + bm.image_data[i] = white; } + return bm; } - return out; -} + static Bitmap decode(std::span data) { + Bitmap bm{}; + auto view{parse_bfl(data)}; + if (!view) + return bm; -struct BFLHeader { - uint16_t w{}; - uint16_t h{}; - uint8_t flags{}; - uint32_t img_len{}; - uint32_t tra_len{}; + auto &h{view->hdr}; + bool has_alpha{(h.flags & FLAG_HAS_ALPHA) != 0}; + bool img_was_raw{(h.flags & FLAG_IMG_RAW) != 0}; + bool tra_was_raw{(h.flags & FLAG_TRA_RAW) != 0}; + bool img_no_lz{(h.flags & FLAG_IMG_NOLZ) != 0}; + bool tra_no_lz{(h.flags & FLAG_TRA_NOLZ) != 0}; + + std::vector img_stream = + img_no_lz ? std::vector(view->img_c.begin(), view->img_c.end()) + : lzss_decompress(view->img_c); + std::vector tra_stream = + has_alpha ? (tra_no_lz ? std::vector(view->tra_c.begin(), + view->tra_c.end()) + : lzss_decompress(view->tra_c)) + : std::vector{}; + + size_t total_bits{(size_t)h.w * (size_t)h.h}; + auto img_bits{img_was_raw ? decode_raw(img_stream, total_bits) + : rle_decode(img_stream, total_bits)}; + std::optional> tra_bits; + if (has_alpha) + tra_bits = tra_was_raw ? decode_raw(tra_stream, total_bits) + : rle_decode(tra_stream, total_bits); + + bm.width = h.w; + bm.height = h.h; + bm.image_data = std::move(img_bits); + bm.transparency_data = std::move(tra_bits); + return bm; + } }; -static bool parse_bfl_header(std::span data, BFLHeader &hdr) { - // minimal header: "BFL" + w + h + flags + lenI + lenT - if (data.size() < 3 + 2 + 2 + 1 + 4 + 4) - return false; - if (!(data[0] == 'B' && data[1] == 'F' && data[2] == 'L')) - return false; - - size_t off = 3; - hdr.w = read_le16(data, off); - off += 2; - hdr.h = read_le16(data, off); - off += 2; - hdr.flags = data[off++]; - hdr.img_len = read_le32(data, off); - off += 4; - hdr.tra_len = read_le32(data, off); - off += 4; - - if (off + hdr.img_len > data.size()) - return false; - if (off + hdr.img_len + hdr.tra_len > data.size()) - return false; - return true; -} - static void print_bfl_header(BFLHeader const &h) { std::println("BFL header:"); std::println(" width : {}", h.w); @@ -504,23 +408,7 @@ static void print_bfl_header(BFLHeader const &h) { std::println(" alpha bytes : {}", h.tra_len); } -// CLI - -auto read_entire_file(std::filesystem::path const &path) - -> std::vector { - std::ifstream stream(path, std::ios::in | std::ios::binary); - std::vector const contents((std::istreambuf_iterator(stream)), - std::istreambuf_iterator()); - return contents; -} - -auto write_entire_file(std::filesystem::path const &path, - std::span const &data) -> void { - std::ofstream stream(path, std::ios::out | std::ios::binary); - stream.write(reinterpret_cast(data.data()), data.size()); -} - -auto main(int const argc, char const *argv[]) -> int { +int main(int argc, char const *argv[]) { if (argc < 2) { std::println(stderr, "Usage:\n" @@ -531,14 +419,13 @@ auto main(int const argc, char const *argv[]) -> int { } if (argc == 3 && std::string_view(argv[1]) == "header") { - std::filesystem::path filepath{argv[2]}; - auto bytes = read_entire_file(filepath); - BFLHeader hdr{}; - if (!parse_bfl_header(bytes, hdr)) { - std::println(stderr, "Invalid or truncated BFL: {}", filepath.string()); + auto bytes{read_file(argv[2])}; + auto v{parse_bfl(bytes)}; + if (!v) { + std::println(stderr, "Invalid or truncated BFL: {}", argv[2]); return 2; } - print_bfl_header(hdr); + print_bfl_header(v->hdr); return 0; } @@ -551,37 +438,31 @@ auto main(int const argc, char const *argv[]) -> int { return 1; } - Bitmap input; + std::filesystem::path in{argv[1]}, outp{argv[2]}; + Bitmap bmp; - std::filesystem::path output_filepath{argv[2]}; - std::filesystem::path filepath{argv[1]}; - if (filepath.extension() == ".bfl") { - input = Bitmap::decode(read_entire_file(filepath)); + if (in.extension() == ".bfl") { + bmp = Bitmap::decode(read_file(in)); } else { - int width, height, comp; - unsigned char *raw_data = - stbi_load(filepath.string().c_str(), &width, &height, &comp, 4); - if (!raw_data) { - std::println(stderr, "stbi_load failed on {}", filepath.string()); + int w, h, comp; + unsigned char *raw{stbi_load(in.string().c_str(), &w, &h, &comp, 4)}; + if (!raw) { + std::println(stderr, "stbi_load failed on {}", in.string()); return 1; } - - std::span data{reinterpret_cast(raw_data), - static_cast(width) * - static_cast(height)}; - input = Bitmap::from_rgba(data, width, height); - - stbi_image_free(raw_data); + std::span px{reinterpret_cast(raw), + (size_t)w * (size_t)h}; + bmp = Bitmap::from_rgba(px, w, h); + stbi_image_free(raw); } - if (output_filepath.extension() == ".bfl") { - auto const input_bin = input.encode(); - std::span d{input_bin.data(), input_bin.size()}; - write_entire_file(output_filepath, d); + if (outp.extension() == ".bfl") { + auto bin{bmp.encode()}; + write_file(outp, std::span(bin.data(), bin.size())); } else { - auto rgba = input.to_rgba(); - stbi_write_png(output_filepath.string().c_str(), input.width, input.height, - 4, rgba.data(), input.width * 4); + auto rgba{bmp.to_rgba()}; + stbi_write_png(outp.string().c_str(), bmp.width, bmp.height, 4, rgba.data(), + bmp.width * 4); } return 0; }