Porytiles
Loading...
Searching...
No Matches
porymap_artifact_parsers.cpp
Go to the documentation of this file.
2
3#include <cstdint>
4#include <format>
5#include <fstream>
6#include <iterator>
7
8namespace {
9
10using namespace porytiles;
11
21[[nodiscard]] std::uint32_t pack_metatile_attribute(const MetatileAttribute &attribute, const Schema &schema)
22{
23 std::uint32_t raw = 0;
24 for (const Field &field : schema.fields()) {
25 const std::uint32_t value = field.packs_layer_type() ? static_cast<std::uint32_t>(attribute.layer_type())
26 : attribute.fields().contains(field.name()) ? attribute.field(field.name())
27 : field.default_value();
28 raw |= (value << field.offset()) & field.mask();
29 }
30 return raw;
31}
32
41[[nodiscard]] ChainableResult<MetatileAttribute> unpack_metatile_attribute(std::uint32_t raw, const Schema &schema)
42{
43 MetatileAttribute attribute{};
44 for (const Field &field : schema.value_fields()) {
45 attribute.field(field.name(), (raw & field.mask()) >> field.offset());
46 }
47
48 if (const Field *layer_field = schema.layer_type_field(); layer_field != nullptr) {
50 layer_type, layer_type_from_int((raw & layer_field->mask()) >> layer_field->offset()), MetatileAttribute);
51 attribute.layer_type(layer_type);
52 }
53
54 return attribute;
55}
56
57} // namespace
58
59namespace porytiles {
60
62{
63 std::ifstream metatiles_bin{path, std::ios::binary};
64 if (!metatiles_bin) {
65 return FormattableError{"Failed to open metatiles.bin: '{}'.", FormatParam{path.string(), Style::bold}};
66 }
67
68 const std::vector<unsigned char> data_buf{std::istreambuf_iterator(metatiles_bin), {}};
69
70 if (data_buf.size() % 2 != 0) {
71 return FormattableError{"Size of metatiles.bin is not a multiple of 2 bytes, possibly corrupted."};
72 }
73
74 std::vector<TilemapEntry> entries;
75 entries.reserve(data_buf.size() / 2);
76
77 for (std::size_t byte_index = 0; byte_index < data_buf.size(); byte_index += 2) {
78 TilemapEntry entry{};
79 const std::uint16_t lower_byte = data_buf.at(byte_index);
80 const std::uint16_t upper_byte = data_buf.at(byte_index + 1);
81 const std::uint16_t entry_bits = (upper_byte << 8) | lower_byte;
82
83 // Metatile BIN Structure
84 // The metatiles.bin file contains a sequence of tilemap entries, which are each two bytes with the following
85 // structure:
86 //
87 // 0000 00XX XXXX XXXX
88 // least significant 10 bits are the tile index
89 //
90 // 0000 0X00 0000 0000
91 // 11th bit is the hflip bit
92 //
93 // 0000 X000 0000 0000
94 // 12th bit is the vflip bit
95 //
96 // XXXX 0000 0000 0000
97 // top 4 bits are palette index
98
99 entry.tile_index(entry_bits & 0x03FF);
100 entry.h_flip((entry_bits >> 10) & 0x0001);
101 entry.v_flip((entry_bits >> 11) & 0x0001);
102 entry.palette_index((entry_bits >> 12) & 0x000F);
103
104 entries.push_back(std::move(entry));
105 }
106
107 return entries;
108}
109
111parse_metatile_attributes(const std::filesystem::path &path, const Schema &schema)
112{
113 std::ifstream metatile_attribute_bin{path, std::ios::binary};
114 if (!metatile_attribute_bin) {
115 return FormattableError{
116 "Failed to open metatile_attributes.bin: '{}'.", FormatParam{path.string(), Style::bold}};
117 }
118
119 const std::vector<unsigned char> data_buf{std::istreambuf_iterator(metatile_attribute_bin), {}};
120
121 const std::size_t attribute_bytes = schema.attribute_bytes();
122 if (data_buf.size() % attribute_bytes != 0) {
123 return FormattableError{
124 "Size of metatile_attributes.bin is not a multiple of {} bytes, possibly corrupted.",
125 FormatParam{attribute_bytes, Style::bold}};
126 }
127
128 std::vector<MetatileAttribute> attributes;
129 const std::size_t metatile_count = data_buf.size() / attribute_bytes;
130 attributes.reserve(metatile_count);
131
132 for (std::size_t metatile_index = 0; metatile_index < metatile_count; metatile_index++) {
133 const std::size_t base = metatile_index * attribute_bytes;
134 std::uint32_t raw = 0;
135 for (std::size_t byte_index = 0; byte_index < attribute_bytes; byte_index++) {
136 raw |= static_cast<std::uint32_t>(data_buf.at(base + byte_index)) << (byte_index * 8);
137 }
138
139 auto attribute_result = unpack_metatile_attribute(raw, schema);
140 if (!attribute_result.has_value()) {
142 FormattableError{"Invalid layer type for metatile '{}'.", FormatParam{metatile_index, Style::bold}},
143 attribute_result};
144 }
145 attributes.push_back(std::move(attribute_result).value());
146 }
147
148 return attributes;
149}
150
152 const std::vector<MetatileAttribute> &attributes, const std::filesystem::path &path, const Schema &schema)
153{
154 std::ofstream out{path, std::ios::binary};
155 if (!out) {
156 return FormattableError{
157 "Failed to open metatile_attributes.bin for writing: '{}'.", FormatParam{path.string(), Style::bold}};
158 }
159
160 const std::size_t attribute_bytes = schema.attribute_bytes();
161 for (const auto &attribute : attributes) {
162 const std::uint32_t raw = pack_metatile_attribute(attribute, schema);
163 for (std::size_t byte_index = 0; byte_index < attribute_bytes; byte_index++) {
164 out << static_cast<std::uint8_t>(raw >> (byte_index * 8));
165 }
166 }
167 out.flush();
168 return {};
169}
170
172load_indexed_png(const std::filesystem::path &path, const PngIndexedImageLoader &loader)
173{
174 auto image_result = loader.load_from_file(path);
175 if (!image_result.has_value()) {
177 FormattableError{"Failed to load indexed PNG: '{}'.", FormatParam{path.string(), Style::bold}},
178 image_result};
179 }
180 return std::move(image_result.value());
181}
182
184load_porymap_palette(const std::filesystem::path &path, const FilePaletteLoader &loader)
185{
186 auto palette_result = loader.load(path);
187 if (!palette_result.has_value()) {
189 FormattableError{"Failed to load palette file: '{}'.", FormatParam{path.string(), Style::bold}},
190 palette_result};
191 }
192 return palette_result.value();
193}
194
195} // namespace porytiles
#define PT_TRY_ASSIGN_PASS_ERR(var, expr, return_type)
Unwraps a ChainableResult, passing through the error chain with an empty FormattableError when types ...
A result type that maintains a chainable sequence of errors for debugging and error reporting.
One named bit-field within a metatile attribute layout.
A service interface that loads a fixed-length Palette from a given file.
virtual ChainableResult< Palette< Rgba32, palette::max_size > > load(const std::filesystem::path &path) const =0
A text parameter with associated styling for formatted output.
General-purpose error implementation with formatted message support.
Definition error.hpp:57
The attributes of a single metatile, modeled as a map of named field values.
std::uint32_t field(std::string_view field_name) const
Returns the value of a named field, or 0 if the field is absent.
An image loader that reads PNG files to create an Image with an index pixel type.
ChainableResult< std::unique_ptr< Image< IndexPixel > > > load_from_file(const std::filesystem::path &path) const
A validated metatile attribute layout: an ordered set of non-overlapping fields.
const Field * layer_type_field() const
Returns the field carrying the layer_type role, or nullptr when the layer type is disabled.
std::size_t attribute_bytes() const
static const Style bold
Bold text formatting.
Represents a tilemap entry referencing a tile with palette and flip attributes.
std::size_t tile_index() const
std::string name
ChainableResult< void > save_metatile_attributes_bin(const std::vector< MetatileAttribute > &attributes, const std::filesystem::path &path, const Schema &schema)
Writes metatile attributes to a metatile_attributes.bin file according to a schema.
ChainableResult< LayerType > layer_type_from_int(unsigned int i)
Converts an integer to LayerType.
Definition layer.hpp:110
ChainableResult< Palette< Rgba32, palette::max_size > > load_porymap_palette(const std::filesystem::path &path, const FilePaletteLoader &loader)
Loads a Porymap palette file (e.g., 00.pal).
ChainableResult< std::vector< TilemapEntry > > parse_metatiles_bin(const std::filesystem::path &path)
Parses a metatiles.bin file into TilemapEntry objects.
ChainableResult< std::vector< MetatileAttribute > > parse_metatile_attributes(const std::filesystem::path &path, const Schema &schema)
Parses a metatile_attributes.bin file according to a metatile attribute schema.
ChainableResult< std::unique_ptr< Image< IndexPixel > > > load_indexed_png(const std::filesystem::path &path, const PngIndexedImageLoader &loader)
Loads an indexed PNG file (e.g., tiles.png).