diff options
author | Hop311 <hop3114@gmail.com> | 2023-05-22 11:36:23 +0200 |
---|---|---|
committer | Hop311 <hop3114@gmail.com> | 2023-05-22 11:36:23 +0200 |
commit | 7874702f30d5855319faf197b10aed31f07f5e27 (patch) | |
tree | 136a221eb5e7c895c8219778b3d206f2ed9e8e7f | |
parent | 212d591c31f4200b06d38e98b23c5c2bccde1772 (diff) |
BMP palette parser + Logger macro
-rw-r--r-- | src/openvic/Date.cpp | 8 | ||||
-rw-r--r-- | src/openvic/GameManager.cpp | 2 | ||||
-rw-r--r-- | src/openvic/Types.hpp | 2 | ||||
-rw-r--r-- | src/openvic/map/Building.cpp | 2 | ||||
-rw-r--r-- | src/openvic/map/Map.cpp | 22 | ||||
-rw-r--r-- | src/openvic/map/Map.hpp | 2 | ||||
-rw-r--r-- | src/openvic/utility/BMP.cpp | 162 | ||||
-rw-r--r-- | src/openvic/utility/BMP.hpp | 48 | ||||
-rw-r--r-- | src/openvic/utility/Logger.cpp (renamed from src/openvic/Logger.cpp) | 8 | ||||
-rw-r--r-- | src/openvic/utility/Logger.hpp (renamed from src/openvic/Logger.hpp) | 41 |
10 files changed, 253 insertions, 44 deletions
diff --git a/src/openvic/Date.cpp b/src/openvic/Date.cpp index db1633f..f7bf9a3 100644 --- a/src/openvic/Date.cpp +++ b/src/openvic/Date.cpp @@ -3,7 +3,7 @@ #include <algorithm> #include <cctype> -#include "Logger.hpp" +#include "utility/Logger.hpp" using namespace OpenVic; @@ -139,17 +139,17 @@ Date Date::from_string(std::string const& date) { size_t first_pos = 0; while (first_pos < date.length() && std::isdigit(date[first_pos++])); - year = atoi(date.substr(0, first_pos).c_str()); + year = stoi(date.substr(0, first_pos)); if (first_pos < date.length()) { if (date[first_pos] == '.') { size_t second_pos = first_pos + 1; while (second_pos < date.length() && std::isdigit(date[second_pos++])); - month = atoi(date.substr(first_pos, second_pos - first_pos).c_str()); + month = stoi(date.substr(first_pos, second_pos - first_pos)); if (second_pos < date.length()) { if (date[second_pos] == '.') { size_t third_pos = second_pos + 1; while (third_pos < date.length() && std::isdigit(date[third_pos++])); - day = atoi(date.substr(second_pos, third_pos - second_pos).c_str()); + day = stoi(date.substr(second_pos, third_pos - second_pos)); if (third_pos < date.length()) Logger::error("Unexpected string \"", date.substr(third_pos), "\" at the end of date ", date); } else Logger::error("Unexpected character \"", date[second_pos], "\" in date ", date); diff --git a/src/openvic/GameManager.cpp b/src/openvic/GameManager.cpp index 6b32e09..b2a9c27 100644 --- a/src/openvic/GameManager.cpp +++ b/src/openvic/GameManager.cpp @@ -1,6 +1,6 @@ #include "GameManager.hpp" -#include "Logger.hpp" +#include "utility/Logger.hpp" using namespace OpenVic; diff --git a/src/openvic/Types.hpp b/src/openvic/Types.hpp index e332bf2..fe22dc9 100644 --- a/src/openvic/Types.hpp +++ b/src/openvic/Types.hpp @@ -5,7 +5,7 @@ #include <map> #include <vector> -#include "Logger.hpp" +#include "utility/Logger.hpp" namespace OpenVic { // Represents a 24-bit RGB integer OR a 32-bit ARGB integer diff --git a/src/openvic/map/Building.cpp b/src/openvic/map/Building.cpp index 1513e06..317ccdf 100644 --- a/src/openvic/map/Building.cpp +++ b/src/openvic/map/Building.cpp @@ -2,7 +2,7 @@ #include <cassert> -#include "../Logger.hpp" +#include "../utility/Logger.hpp" #include "Province.hpp" using namespace OpenVic; diff --git a/src/openvic/map/Map.cpp b/src/openvic/map/Map.cpp index 8d5b9e7..2efee32 100644 --- a/src/openvic/map/Map.cpp +++ b/src/openvic/map/Map.cpp @@ -3,8 +3,8 @@ #include <cassert> #include <unordered_set> -#include "../Logger.hpp" #include "../economy/Good.hpp" +#include "../utility/Logger.hpp" using namespace OpenVic; @@ -15,6 +15,8 @@ Mapmode::Mapmode(index_t new_index, std::string const& new_identifier, colour_fu assert(colour_func != nullptr); } +const Mapmode Mapmode::ERROR_MAPMODE { 0, "mapmode_error", [](Map const& map, Province const& province) -> colour_t { return 0xFFFF0000; } }; + Mapmode::index_t Mapmode::get_index() const { return index; } @@ -180,7 +182,8 @@ Region const* Map::get_region_by_identifier(std::string const& identifier) const } static colour_t colour_at(uint8_t const* colour_data, int32_t idx) { - return (colour_data[idx * 3] << 16) | (colour_data[idx * 3 + 1] << 8) | colour_data[idx * 3 + 2]; + idx *= 3; + return (colour_data[idx] << 16) | (colour_data[idx + 1] << 8) | colour_data[idx + 2]; } return_t Map::generate_province_shape_image(size_t new_width, size_t new_height, uint8_t const* colour_data, @@ -313,10 +316,17 @@ return_t Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target) Logger::error("Mapmode colour target pointer is null!"); return FAILURE; } + return_t ret = SUCCESS; Mapmode const* mapmode = mapmodes.get_item_by_index(index); if (mapmode == nullptr) { - Logger::error("Invalid mapmode index: ", index); - return FAILURE; + // Not an error if mapmodes haven't yet been loaded, + // e.g. if we want to allocate the province colour + // texture before mapmodes are loaded. + if (!(mapmodes.get_item_count() == 0 && index == 0)) { + Logger::error("Invalid mapmode index: ", index); + ret = FAILURE; + } + mapmode = &Mapmode::ERROR_MAPMODE; } // Skip past Province::NULL_INDEX for (size_t i = 0; i < MAPMODE_COLOUR_SIZE; ++i) @@ -328,14 +338,14 @@ return_t Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target) *target++ = colour & FULL_COLOUR; *target++ = (colour >> 24) & FULL_COLOUR; } - return SUCCESS; + return ret; } return_t Map::setup(GoodManager const& good_manager, BuildingManager const& building_manager) { return_t ret = SUCCESS; for (Province& province : provinces.get_items()) { // Set all land provinces to have an RGO based on their index to test them - if (!province.is_water()) + if (!province.is_water() && good_manager.get_good_count() > 0) province.rgo = good_manager.get_good_by_index(province.get_index() % good_manager.get_good_count()); if (building_manager.generate_province_buildings(province) != SUCCESS) ret = FAILURE; } diff --git a/src/openvic/map/Map.hpp b/src/openvic/map/Map.hpp index a7c29bb..3533a14 100644 --- a/src/openvic/map/Map.hpp +++ b/src/openvic/map/Map.hpp @@ -19,6 +19,8 @@ namespace OpenVic { Mapmode(index_t new_index, std::string const& new_identifier, colour_func_t new_colour_func); public: + static const Mapmode ERROR_MAPMODE; + index_t get_index() const; colour_t get_colour(Map const& map, Province const& province) const; }; diff --git a/src/openvic/utility/BMP.cpp b/src/openvic/utility/BMP.cpp new file mode 100644 index 0000000..eb3e2b1 --- /dev/null +++ b/src/openvic/utility/BMP.cpp @@ -0,0 +1,162 @@ +#include "BMP.hpp" + +#include <set> + +#include "Logger.hpp" + +using namespace OpenVic; + +BMP::~BMP() { + close(); +} + +return_t BMP::open(char const* filepath) { + reset(); + errno = 0; + file = fopen(filepath, "rb"); + if (file == nullptr || errno != 0) { + Logger::error("Failed to open BMP file \"", filepath, "\" (errno = ", errno, ")"); + file = nullptr; + return FAILURE; + } + return SUCCESS; +} + +return_t BMP::read_header() { + if (header_validated) { + Logger::error("BMP header already validated!"); + return FAILURE; + } + if (file == nullptr) { + Logger::error("Cannot read BMP header before opening a file"); + return FAILURE; + } + if (fseek(file, 0, SEEK_SET) != 0) { + Logger::error("Failed to move to the beginning of the BMP file!"); + return FAILURE; + } + if (fread(&header, sizeof(header), 1, file) != 1) { + Logger::error("Failed to read BMP header!"); + return FAILURE; + } + return_t ret = SUCCESS; + + // Validate constants + static constexpr uint16_t BMP_SIGNATURE = 0x4d42; + if (header.signature != BMP_SIGNATURE) { + Logger::error("Invalid BMP signature: ", header.signature, " (must be ", BMP_SIGNATURE, ")"); + ret = FAILURE; + } + static constexpr uint32_t DIB_HEADER_SIZE = 40; + if (header.dib_header_size != DIB_HEADER_SIZE) { + Logger::error("Invalid BMP DIB header size: ", header.dib_header_size, " (must be ", DIB_HEADER_SIZE, ")"); + ret = FAILURE; + } + static constexpr uint16_t NUM_PLANES = 1; + if (header.num_planes != NUM_PLANES) { + Logger::error("Invalid BMP plane count: ", header.num_planes, " (must be ", NUM_PLANES, ")"); + ret = FAILURE; + } + static constexpr uint16_t COMPRESSION = 0; // Only support uncompressed BMPs + if (header.compression != COMPRESSION) { + Logger::error("Invalid BMP compression method: ", header.compression, " (must be ", COMPRESSION, ")"); + ret = FAILURE; + } + + // Validate sizes and dimensions + // TODO - image_size_bytes can be 0 for non-compressed BMPs + if (header.file_size != header.offset + header.image_size_bytes) { + Logger::error("Invalid BMP memory sizes: file size = ", header.file_size, " != ", header.offset + header.image_size_bytes, + " = ", header.offset, " + ", header.image_size_bytes, " = image data offset + image data size"); + ret = FAILURE; + } + // TODO - support negative widths (i.e. horizontal flip) + if (header.width_px <= 0) { + Logger::error("Invalid BMP width: ", header.width_px, " (must be positive)"); + ret = FAILURE; + } + // TODO - support negative heights (i.e. vertical flip) + if (header.height_px <= 0) { + Logger::error("Invalid BMP height: ", header.height_px, " (must be positive)"); + ret = FAILURE; + } + // TODO - validate x_resolution_ppm + // TODO - validate y_resolution_ppm + + // Validate colours +#define VALID_BITS_PER_PIXEL 1, 2, 4, 8, 16, 24, 32 +#define STR(x) #x + static const std::set<uint16_t> BITS_PER_PIXEL { VALID_BITS_PER_PIXEL }; + if (!BITS_PER_PIXEL.contains(header.bits_per_pixel)) { + Logger::error("Invalid BMP bits per pixel: ", header.bits_per_pixel, " (must be one of " STR(VALID_BITS_PER_PIXEL) ")"); + ret = FAILURE; + } +#undef VALID_BITS_PER_PIXEL +#undef STR + static constexpr uint16_t PALETTE_BITS_PER_PIXEL_LIMIT = 8; + if (header.num_colours != 0 && header.bits_per_pixel > PALETTE_BITS_PER_PIXEL_LIMIT) { + Logger::error("Invalid BMP palette size: ", header.num_colours, " (should be 0 as bits per pixel is ", header.bits_per_pixel, " > 8)"); + ret = FAILURE; + } + // TODO - validate important_colours + + palette_size = header.bits_per_pixel > PALETTE_BITS_PER_PIXEL_LIMIT ? 0 + // Use header.num_colours if it's greater than 0 and at most 1 << header.bits_per_pixel + : 0 < header.num_colours && header.num_colours - 1 >> header.bits_per_pixel == 0 ? header.num_colours + : 1 << header.bits_per_pixel; + + const uint32_t expected_offset = palette_size * PALETTE_COLOUR_SIZE + sizeof(header); + if (header.offset != expected_offset) { + Logger::error("Invalid BMP image data offset: ", header.offset, " (should be ", expected_offset, ")"); + ret = FAILURE; + } + + header_validated = ret == SUCCESS; + return ret; +} + +return_t BMP::read_palette() { + if (file == nullptr) { + Logger::error("Cannot read BMP palette before opening a file"); + return FAILURE; + } + if (!header_validated) { + Logger::error("Cannot read palette before BMP header is validated!"); + return FAILURE; + } + if (palette_size == 0) { + Logger::error("Cannot read BMP palette - header indicates this file doesn't have one"); + return FAILURE; + } + if (fseek(file, sizeof(header), SEEK_SET) != 0) { + Logger::error("Failed to move to the palette in the BMP file!"); + return FAILURE; + } + palette.resize(palette_size); + if (fread(palette.data(), palette_size * PALETTE_COLOUR_SIZE, 1, file) != 1) { + Logger::error("Failed to read BMP header!"); + palette.clear(); + return FAILURE; + } + return SUCCESS; +} + +void BMP::close() { + if (file != nullptr) { + if (fclose(file) != 0) + Logger::error("Failed to close BMP!"); + file = nullptr; + } +} + +void BMP::reset() { + close(); + memset(&header, 0, sizeof(header)); + header_validated = false; + palette_size = 0; + palette.clear(); +} + +std::vector<colour_t> const& BMP::get_palette() const { + return palette; +} diff --git a/src/openvic/utility/BMP.hpp b/src/openvic/utility/BMP.hpp new file mode 100644 index 0000000..c6f5815 --- /dev/null +++ b/src/openvic/utility/BMP.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "../Types.hpp" + +namespace OpenVic { + class BMP { +#pragma pack(push) +#pragma pack(1) + struct header_t { + uint16_t signature; // Signature: 0x4d42 (or 'B' 'M') + uint32_t file_size; // File size in bytes + uint16_t reserved1; // Not used + uint16_t reserved2; // Not used + uint32_t offset; // Offset to image data in bytes from beginning of file + uint32_t dib_header_size; // DIB header size in bytes + int32_t width_px; // Width of the image + int32_t height_px; // Height of image + uint16_t num_planes; // Number of colour planes + uint16_t bits_per_pixel; // Bits per pixel + uint32_t compression; // Compression type + uint32_t image_size_bytes; // Image size in bytes + int32_t x_resolution_ppm; // Pixels per meter + int32_t y_resolution_ppm; // Pixels per meter + uint32_t num_colours; // Number of colours + uint32_t important_colours; // Important colours + } header; +#pragma pack(pop) + + FILE* file = nullptr; + bool header_validated = false; + uint32_t palette_size = 0; + std::vector<colour_t> palette; + + public: + static constexpr uint32_t PALETTE_COLOUR_SIZE = sizeof(colour_t); + + BMP() = default; + ~BMP(); + + return_t open(char const* filepath); + return_t read_header(); + return_t read_palette(); + void close(); + void reset(); + + std::vector<colour_t> const& get_palette() const; + }; +} diff --git a/src/openvic/Logger.cpp b/src/openvic/utility/Logger.cpp index f93b8f1..fe97473 100644 --- a/src/openvic/Logger.cpp +++ b/src/openvic/utility/Logger.cpp @@ -16,11 +16,3 @@ char const* Logger::get_filename(char const* filepath) { } return last_slash; } - -void Logger::set_info_func(log_func_t log_func) { - info_func = log_func; -} - -void Logger::set_error_func(log_func_t log_func) { - error_func = log_func; -} diff --git a/src/openvic/Logger.hpp b/src/openvic/utility/Logger.hpp index 923087e..ac82445 100644 --- a/src/openvic/Logger.hpp +++ b/src/openvic/utility/Logger.hpp @@ -40,8 +40,6 @@ namespace OpenVic { using source_location = OpenVic::source_location; #endif - static log_func_t info_func, error_func; - static char const* get_filename(char const* filepath); template<typename... Ts> @@ -58,28 +56,25 @@ namespace OpenVic { } }; - public: - static void set_info_func(log_func_t log_func); - static void set_error_func(log_func_t log_func); - - template<typename... Ts> - struct info { - info(Ts&&... ts, source_location const& location = source_location::current()) { - log<Ts...> { info_func, std::forward<Ts>(ts)..., location }; - } - }; - - template<typename... Ts> - info(Ts&&...) -> info<Ts...>; +#define LOG_FUNC(name) \ + private: \ + static log_func_t name##_func; \ + public: \ + static void set_##name##_func(log_func_t log_func) { \ + name##_func = log_func; \ + } \ + template <typename... Ts> \ + struct name { \ + name(Ts&&... ts, source_location const& location = source_location::current()) { \ + log<Ts...>{ name##_func, std::forward<Ts>(ts)..., location }; \ + } \ + }; \ + template <typename... Ts> \ + name(Ts&&...) -> name<Ts...>; - template<typename... Ts> - struct error { - error(Ts&&... ts, source_location const& location = source_location::current()) { - log<Ts...> { error_func, std::forward<Ts>(ts)..., location }; - } - }; + LOG_FUNC(info) + LOG_FUNC(error) - template<typename... Ts> - error(Ts&&...) -> error<Ts...>; +#undef LOG_FUNC }; } |