Porytiles
Loading...
Searching...
No Matches
porymap_artifact_parsers.cpp
Go to the documentation of this file.
2
3#include <format>
4#include <fstream>
5#include <iterator>
6
7namespace porytiles {
8
10{
11 std::ifstream metatiles_bin{path, std::ios::binary};
12 if (!metatiles_bin) {
13 return FormattableError{"Failed to open metatiles.bin: '{}'.", FormatParam{path.string(), Style::bold}};
14 }
15
16 const std::vector<unsigned char> data_buf{std::istreambuf_iterator(metatiles_bin), {}};
17
18 if (data_buf.size() % 2 != 0) {
19 return FormattableError{"Size of metatiles.bin is not a multiple of 2 bytes, possibly corrupted."};
20 }
21
22 std::vector<TilemapEntry> entries;
23 entries.reserve(data_buf.size() / 2);
24
25 for (std::size_t byte_index = 0; byte_index < data_buf.size(); byte_index += 2) {
26 TilemapEntry entry{};
27 const std::uint16_t lower_byte = data_buf.at(byte_index);
28 const std::uint16_t upper_byte = data_buf.at(byte_index + 1);
29 const std::uint16_t entry_bits = (upper_byte << 8) | lower_byte;
30
31 // Metatile BIN Structure
32 // The metatiles.bin file contains a sequence of tilemap entries, which are each two bytes with the following
33 // structure:
34 //
35 // 0000 00XX XXXX XXXX
36 // least significant 10 bits are the tile index
37 //
38 // 0000 0X00 0000 0000
39 // 11th bit is the hflip bit
40 //
41 // 0000 X000 0000 0000
42 // 12th bit is the vflip bit
43 //
44 // XXXX 0000 0000 0000
45 // top 4 bits are pal index
46
47 entry.tile_index(entry_bits & 0x03FF);
48 entry.h_flip((entry_bits >> 10) & 0x0001);
49 entry.v_flip((entry_bits >> 11) & 0x0001);
50 entry.pal_index((entry_bits >> 12) & 0x000F);
51
52 entries.push_back(std::move(entry));
53 }
54
55 return entries;
56}
57
59{
60 std::ifstream metatile_attr_bin{path, std::ios::binary};
61 if (!metatile_attr_bin) {
62 return FormattableError{
63 "Failed to open metatile_attributes.bin: '{}'.", FormatParam{path.string(), Style::bold}};
64 }
65
66 const std::vector<unsigned char> data_buf{std::istreambuf_iterator(metatile_attr_bin), {}};
67
68 if (data_buf.size() % attr::bytes_per_attr_emerald != 0) {
69 return FormattableError{
70 "Size of metatile_attributes.bin is not a multiple of {} bytes, possibly corrupted.",
72 }
73
74 std::vector<MetatileAttribute> attributes;
75 const std::size_t metatile_count = data_buf.size() / attr::bytes_per_attr_emerald;
76 attributes.reserve(metatile_count);
77
78 for (std::size_t metatile_index = 0; metatile_index < metatile_count; metatile_index++) {
79 std::uint16_t byte0 = data_buf.at((metatile_index * attr::bytes_per_attr_emerald));
80 std::uint16_t byte1 = data_buf.at((metatile_index * attr::bytes_per_attr_emerald) + 1);
81 std::uint16_t attribute = (byte1 << 8) | byte0;
82
83 auto layer_type_result = layer_type_from_int(attribute >> 12 & 0x000F);
84 if (!layer_type_result.has_value()) {
86 FormattableError{"Invalid layer type for metatile '{}'.", FormatParam{metatile_index, Style::bold}},
87 layer_type_result};
88 }
89 MetatileAttribute metatile_attribute{layer_type_result.value(), static_cast<std::uint16_t>(attribute & 0x00FF)};
90 attributes.push_back(std::move(metatile_attribute));
91 }
92
93 return attributes;
94}
95
97{
98 std::ifstream metatile_attr_bin{path, std::ios::binary};
99 if (!metatile_attr_bin) {
100 return FormattableError{
101 "Failed to open metatile_attributes.bin: '{}'.", FormatParam{path.string(), Style::bold}};
102 }
103
104 const std::vector<unsigned char> data_buf{std::istreambuf_iterator(metatile_attr_bin), {}};
105
106 if (data_buf.size() % attr::bytes_per_attr_firered != 0) {
107 return FormattableError{
108 "Size of metatile_attributes.bin is not a multiple of {} bytes, possibly corrupted.",
110 }
111
112 std::vector<MetatileAttribute> attributes;
113 const std::size_t metatile_count = data_buf.size() / attr::bytes_per_attr_firered;
114 attributes.reserve(metatile_count);
115
116 for (std::size_t metatile_index = 0; metatile_index < metatile_count; metatile_index++) {
117 const std::size_t base = metatile_index * attr::bytes_per_attr_firered;
118 const std::uint32_t byte0 = data_buf.at(base);
119 const std::uint32_t byte1 = data_buf.at(base + 1);
120 const std::uint32_t byte2 = data_buf.at(base + 2);
121 const std::uint32_t byte3 = data_buf.at(base + 3);
122 const std::uint32_t attribute = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0;
123
124 // FireRed attribute bit layout (from fieldmap.c):
125 // Bits 0-8: behavior (0x000001FF)
126 // Bits 9-13: terrain (0x00003E00)
127 // Bits 14-17: attribute_2 (0x0003C000)
128 // Bits 18-23: attribute_3 (0x00FC0000)
129 // Bits 24-26: encounter_type (0x07000000)
130 // Bits 27-28: attribute_5 (0x18000000)
131 // Bits 29-30: layer_type (0x60000000)
132 // Bit 31: attribute_7 (0x80000000)
133 const auto behavior = static_cast<std::uint16_t>(attribute & 0x000001FF);
134 const auto terrain = static_cast<std::uint8_t>((attribute >> 9) & 0x1F);
135 const auto attribute_2 = static_cast<std::uint8_t>((attribute >> 14) & 0x0F);
136 const auto attribute_3 = static_cast<std::uint8_t>((attribute >> 18) & 0x3F);
137 const auto encounter_type = static_cast<std::uint8_t>((attribute >> 24) & 0x07);
138 const auto attribute_5 = static_cast<std::uint8_t>((attribute >> 27) & 0x03);
139 const auto layer_type_raw = static_cast<unsigned int>((attribute >> 29) & 0x03);
140 const bool attribute_7 = (attribute >> 31) & 0x01;
141
142 auto layer_type_result = layer_type_from_int(layer_type_raw);
143 if (!layer_type_result.has_value()) {
145 FormattableError{"Invalid layer type for metatile '{}'.", FormatParam{metatile_index, Style::bold}},
146 layer_type_result};
147 }
148
149 MetatileAttribute metatile_attribute{
150 layer_type_result.value(),
151 behavior,
152 terrain,
153 encounter_type,
154 attribute_2,
155 attribute_3,
156 attribute_5,
157 attribute_7};
158 attributes.push_back(std::move(metatile_attribute));
159 }
160
161 return attributes;
162}
163
164ChainableResult<std::unique_ptr<Image<IndexPixel>>>
165load_indexed_png(const std::filesystem::path &path, const PngIndexedImageLoader &loader)
166{
167 auto image_result = loader.load_from_file(path);
168 if (!image_result.has_value()) {
170 FormattableError{"Failed to load indexed PNG: '{}'.", FormatParam{path.string(), Style::bold}},
171 image_result};
172 }
173 return std::move(image_result.value());
174}
175
176ChainableResult<Palette<Rgba32, pal::max_size>>
177load_porymap_palette(const std::filesystem::path &path, const FilePalLoader &loader)
178{
179 auto pal_result = loader.load(path);
180 if (!pal_result.has_value()) {
182 FormattableError{"Failed to load palette file: '{}'.", FormatParam{path.string(), Style::bold}},
183 pal_result};
184 }
185 return pal_result.value();
186}
187
188} // namespace porytiles
A result type that maintains a chainable sequence of errors for debugging and error reporting.
A service interface that loads a fixed-length Palette from a given file.
virtual ChainableResult< Palette< Rgba32, pal::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:63
Represents the attributes of a single metatile.
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
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
constexpr std::size_t bytes_per_attr_emerald
constexpr std::size_t bytes_per_attr_firered
ChainableResult< std::vector< MetatileAttribute > > parse_firered_metatile_attributes(const std::filesystem::path &path)
Parses a metatile_attributes.bin file for FireRed format.
ChainableResult< LayerType > layer_type_from_int(unsigned int i)
Converts an integer to LayerType.
Definition layer.hpp:125
ChainableResult< Palette< Rgba32, pal::max_size > > load_porymap_palette(const std::filesystem::path &path, const FilePalLoader &loader)
Loads a Porymap palette file (e.g., 00.pal).
ChainableResult< std::vector< MetatileAttribute > > parse_emerald_metatile_attributes(const std::filesystem::path &path)
Parses a metatile_attributes.bin file for Emerald format.
ChainableResult< std::vector< TilemapEntry > > parse_metatiles_bin(const std::filesystem::path &path)
Parses a metatiles.bin file into TilemapEntry objects.
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).