summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hop311 <hop3114@gmail.com>2023-05-22 11:36:23 +0200
committer Hop311 <hop3114@gmail.com>2023-05-22 11:36:23 +0200
commit7874702f30d5855319faf197b10aed31f07f5e27 (patch)
tree136a221eb5e7c895c8219778b3d206f2ed9e8e7f
parent212d591c31f4200b06d38e98b23c5c2bccde1772 (diff)
BMP palette parser + Logger macro
-rw-r--r--src/openvic/Date.cpp8
-rw-r--r--src/openvic/GameManager.cpp2
-rw-r--r--src/openvic/Types.hpp2
-rw-r--r--src/openvic/map/Building.cpp2
-rw-r--r--src/openvic/map/Map.cpp22
-rw-r--r--src/openvic/map/Map.hpp2
-rw-r--r--src/openvic/utility/BMP.cpp162
-rw-r--r--src/openvic/utility/BMP.hpp48
-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
};
}