yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
zsprite.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
2#define YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
3
4#include <cstdint>
5#include <fstream>
6#include <string>
7#include <vector>
8
9#include "absl/status/status.h"
10#include "util/macro.h"
11
12namespace yaze {
13namespace editor {
22namespace zsprite {
23
27inline std::string ReadDotNetString(std::istream& is) {
28 uint32_t length = 0;
29 uint8_t byte;
30 int shift = 0;
31 do {
32 is.read(reinterpret_cast<char*>(&byte), 1);
33 if (!is.good()) return "";
34 length |= (byte & 0x7F) << shift;
35 shift += 7;
36 } while (byte & 0x80);
37
38 std::string result(length, '\0');
39 if (length > 0) {
40 is.read(&result[0], length);
41 }
42 return result;
43}
44
48inline void WriteDotNetString(std::ostream& os, const std::string& str) {
49 uint32_t length = static_cast<uint32_t>(str.size());
50
51 // Write 7-bit encoded length
52 do {
53 uint8_t byte = length & 0x7F;
54 length >>= 7;
55 if (length > 0) {
56 byte |= 0x80; // Set continuation bit
57 }
58 os.write(reinterpret_cast<const char*>(&byte), 1);
59 } while (length > 0);
60
61 // Write string content
62 if (!str.empty()) {
63 os.write(str.data(), str.size());
64 }
65}
66
67struct OamTile {
68 OamTile() = default;
69 OamTile(uint8_t x, uint8_t y, bool mx, bool my, uint16_t id, uint8_t pal,
70 bool s, uint8_t p)
71 : x(x),
72 y(y),
73 mirror_x(mx),
74 mirror_y(my),
75 id(id),
76 palette(pal),
77 size(s),
78 priority(p) {}
79
80 uint8_t x = 0;
81 uint8_t y = 0;
82 bool mirror_x = false;
83 bool mirror_y = false;
84 uint16_t id = 0;
85 uint8_t palette = 0;
86 bool size = false; // false = 8x8, true = 16x16
87 uint8_t priority = 3;
88 uint8_t z = 0;
89};
90
92 AnimationGroup() = default;
93 AnimationGroup(uint8_t fs, uint8_t fe, uint8_t fsp, std::string fn)
94 : frame_name(std::move(fn)),
95 frame_start(fs),
96 frame_end(fe),
97 frame_speed(fsp) {}
98
99 std::string frame_name;
100 uint8_t frame_start = 0;
101 uint8_t frame_end = 0;
102 uint8_t frame_speed = 1;
103 std::vector<OamTile> Tiles;
104};
105
106struct Frame {
107 std::vector<OamTile> Tiles;
108};
109
111 UserRoutine() = default;
112 UserRoutine(std::string n, std::string c)
113 : name(std::move(n)), code(std::move(c)) {}
114
115 std::string name;
116 std::string code;
117};
118
119struct SubEditor {
120 std::vector<Frame> Frames;
121 std::vector<UserRoutine> user_routines;
122};
123
125 bool IsChecked = false;
126 std::string Text;
127};
128
129struct ZSprite {
130 public:
134 absl::Status Load(const std::string& filename) {
135 std::ifstream fs(filename, std::ios::binary);
136 if (!fs.is_open()) {
137 return absl::NotFoundError("File not found: " + filename);
138 }
139
140 // Clear existing data
141 Reset();
142
143 // Read animation count
144 int32_t animation_count = 0;
145 fs.read(reinterpret_cast<char*>(&animation_count), sizeof(int32_t));
146
147 // Read animations
148 for (int i = 0; i < animation_count; i++) {
149 std::string aname = ReadDotNetString(fs);
150 uint8_t afs, afe, afspeed;
151 fs.read(reinterpret_cast<char*>(&afs), sizeof(uint8_t));
152 fs.read(reinterpret_cast<char*>(&afe), sizeof(uint8_t));
153 fs.read(reinterpret_cast<char*>(&afspeed), sizeof(uint8_t));
154 animations.emplace_back(afs, afe, afspeed, aname);
155 }
156
157 // Read frame count
158 int32_t frame_count = 0;
159 fs.read(reinterpret_cast<char*>(&frame_count), sizeof(int32_t));
160
161 // Read frames
162 for (int i = 0; i < frame_count; i++) {
163 editor.Frames.emplace_back();
164
165 int32_t tile_count = 0;
166 fs.read(reinterpret_cast<char*>(&tile_count), sizeof(int32_t));
167
168 for (int j = 0; j < tile_count; j++) {
169 uint16_t tid;
170 uint8_t tpal, tprior, tx, ty, tz;
171 bool tmx, tmy, tsize;
172
173 fs.read(reinterpret_cast<char*>(&tid), sizeof(uint16_t));
174 fs.read(reinterpret_cast<char*>(&tpal), sizeof(uint8_t));
175 fs.read(reinterpret_cast<char*>(&tmx), sizeof(bool));
176 fs.read(reinterpret_cast<char*>(&tmy), sizeof(bool));
177 fs.read(reinterpret_cast<char*>(&tprior), sizeof(uint8_t));
178 fs.read(reinterpret_cast<char*>(&tsize), sizeof(bool));
179 fs.read(reinterpret_cast<char*>(&tx), sizeof(uint8_t));
180 fs.read(reinterpret_cast<char*>(&ty), sizeof(uint8_t));
181 fs.read(reinterpret_cast<char*>(&tz), sizeof(uint8_t));
182
183 OamTile tile(tx, ty, tmx, tmy, tid, tpal, tsize, tprior);
184 tile.z = tz;
185 editor.Frames[i].Tiles.push_back(tile);
186 }
187 }
188
189 // Read 20 sprite boolean properties
190 fs.read(reinterpret_cast<char*>(&property_blockable.IsChecked), 1);
191 fs.read(reinterpret_cast<char*>(&property_canfall.IsChecked), 1);
192 fs.read(reinterpret_cast<char*>(&property_collisionlayer.IsChecked), 1);
193 fs.read(reinterpret_cast<char*>(&property_customdeath.IsChecked), 1);
194 fs.read(reinterpret_cast<char*>(&property_damagesound.IsChecked), 1);
195 fs.read(reinterpret_cast<char*>(&property_deflectarrows.IsChecked), 1);
196 fs.read(reinterpret_cast<char*>(&property_deflectprojectiles.IsChecked), 1);
197 fs.read(reinterpret_cast<char*>(&property_fast.IsChecked), 1);
198 fs.read(reinterpret_cast<char*>(&property_harmless.IsChecked), 1);
199 fs.read(reinterpret_cast<char*>(&property_impervious.IsChecked), 1);
200 fs.read(reinterpret_cast<char*>(&property_imperviousarrow.IsChecked), 1);
201 fs.read(reinterpret_cast<char*>(&property_imperviousmelee.IsChecked), 1);
202 fs.read(reinterpret_cast<char*>(&property_interaction.IsChecked), 1);
203 fs.read(reinterpret_cast<char*>(&property_isboss.IsChecked), 1);
204 fs.read(reinterpret_cast<char*>(&property_persist.IsChecked), 1);
205 fs.read(reinterpret_cast<char*>(&property_shadow.IsChecked), 1);
206 fs.read(reinterpret_cast<char*>(&property_smallshadow.IsChecked), 1);
207 fs.read(reinterpret_cast<char*>(&property_statis.IsChecked), 1);
208 fs.read(reinterpret_cast<char*>(&property_statue.IsChecked), 1);
209 fs.read(reinterpret_cast<char*>(&property_watersprite.IsChecked), 1);
210
211 // Read 6 sprite stat bytes
212 uint8_t prize, palette, oamnbr, hitbox, health, damage;
213 fs.read(reinterpret_cast<char*>(&prize), sizeof(uint8_t));
214 fs.read(reinterpret_cast<char*>(&palette), sizeof(uint8_t));
215 fs.read(reinterpret_cast<char*>(&oamnbr), sizeof(uint8_t));
216 fs.read(reinterpret_cast<char*>(&hitbox), sizeof(uint8_t));
217 fs.read(reinterpret_cast<char*>(&health), sizeof(uint8_t));
218 fs.read(reinterpret_cast<char*>(&damage), sizeof(uint8_t));
219
220 property_prize.Text = std::to_string(prize);
221 property_palette.Text = std::to_string(palette);
222 property_oamnbr.Text = std::to_string(oamnbr);
223 property_hitbox.Text = std::to_string(hitbox);
224 property_health.Text = std::to_string(health);
225 property_damage.Text = std::to_string(damage);
226
227 // Read optional sections (check if more data exists)
228 if (fs.peek() != EOF) {
231
232 int32_t routine_count = 0;
233 fs.read(reinterpret_cast<char*>(&routine_count), sizeof(int32_t));
234
235 for (int i = 0; i < routine_count; i++) {
236 std::string rname = ReadDotNetString(fs);
237 std::string rcode = ReadDotNetString(fs);
238 userRoutines.emplace_back(rname, rcode);
239 }
240 }
241
242 // Read optional sprite ID
243 if (fs.peek() != EOF) {
245 }
246
247 fs.close();
248 return absl::OkStatus();
249 }
250
254 absl::Status Save(const std::string& filename) {
255 std::ofstream fs(filename, std::ios::binary);
256 if (!fs.is_open()) {
257 return absl::InternalError("Failed to open file for writing: " + filename);
258 }
259
260 // Write animation count
261 int32_t anim_count = static_cast<int32_t>(animations.size());
262 fs.write(reinterpret_cast<const char*>(&anim_count), sizeof(int32_t));
263
264 // Write animations
265 for (const auto& anim : animations) {
266 WriteDotNetString(fs, anim.frame_name);
267 fs.write(reinterpret_cast<const char*>(&anim.frame_start), sizeof(uint8_t));
268 fs.write(reinterpret_cast<const char*>(&anim.frame_end), sizeof(uint8_t));
269 fs.write(reinterpret_cast<const char*>(&anim.frame_speed), sizeof(uint8_t));
270 }
271
272 // Write frame count
273 int32_t frame_count = static_cast<int32_t>(editor.Frames.size());
274 fs.write(reinterpret_cast<const char*>(&frame_count), sizeof(int32_t));
275
276 // Write frames
277 for (const auto& frame : editor.Frames) {
278 int32_t tile_count = static_cast<int32_t>(frame.Tiles.size());
279 fs.write(reinterpret_cast<const char*>(&tile_count), sizeof(int32_t));
280
281 for (const auto& tile : frame.Tiles) {
282 fs.write(reinterpret_cast<const char*>(&tile.id), sizeof(uint16_t));
283 fs.write(reinterpret_cast<const char*>(&tile.palette), sizeof(uint8_t));
284 fs.write(reinterpret_cast<const char*>(&tile.mirror_x), sizeof(bool));
285 fs.write(reinterpret_cast<const char*>(&tile.mirror_y), sizeof(bool));
286 fs.write(reinterpret_cast<const char*>(&tile.priority), sizeof(uint8_t));
287 fs.write(reinterpret_cast<const char*>(&tile.size), sizeof(bool));
288 fs.write(reinterpret_cast<const char*>(&tile.x), sizeof(uint8_t));
289 fs.write(reinterpret_cast<const char*>(&tile.y), sizeof(uint8_t));
290 fs.write(reinterpret_cast<const char*>(&tile.z), sizeof(uint8_t));
291 }
292 }
293
294 // Write 20 sprite boolean properties
295 fs.write(reinterpret_cast<const char*>(&property_blockable.IsChecked), 1);
296 fs.write(reinterpret_cast<const char*>(&property_canfall.IsChecked), 1);
297 fs.write(reinterpret_cast<const char*>(&property_collisionlayer.IsChecked), 1);
298 fs.write(reinterpret_cast<const char*>(&property_customdeath.IsChecked), 1);
299 fs.write(reinterpret_cast<const char*>(&property_damagesound.IsChecked), 1);
300 fs.write(reinterpret_cast<const char*>(&property_deflectarrows.IsChecked), 1);
301 fs.write(reinterpret_cast<const char*>(&property_deflectprojectiles.IsChecked), 1);
302 fs.write(reinterpret_cast<const char*>(&property_fast.IsChecked), 1);
303 fs.write(reinterpret_cast<const char*>(&property_harmless.IsChecked), 1);
304 fs.write(reinterpret_cast<const char*>(&property_impervious.IsChecked), 1);
305 fs.write(reinterpret_cast<const char*>(&property_imperviousarrow.IsChecked), 1);
306 fs.write(reinterpret_cast<const char*>(&property_imperviousmelee.IsChecked), 1);
307 fs.write(reinterpret_cast<const char*>(&property_interaction.IsChecked), 1);
308 fs.write(reinterpret_cast<const char*>(&property_isboss.IsChecked), 1);
309 fs.write(reinterpret_cast<const char*>(&property_persist.IsChecked), 1);
310 fs.write(reinterpret_cast<const char*>(&property_shadow.IsChecked), 1);
311 fs.write(reinterpret_cast<const char*>(&property_smallshadow.IsChecked), 1);
312 fs.write(reinterpret_cast<const char*>(&property_statis.IsChecked), 1);
313 fs.write(reinterpret_cast<const char*>(&property_statue.IsChecked), 1);
314 fs.write(reinterpret_cast<const char*>(&property_watersprite.IsChecked), 1);
315
316 // Write 6 sprite stat bytes (parse from Text properties)
317 uint8_t prize = static_cast<uint8_t>(std::stoi(property_prize.Text.empty() ? "0" : property_prize.Text));
318 uint8_t palette = static_cast<uint8_t>(std::stoi(property_palette.Text.empty() ? "0" : property_palette.Text));
319 uint8_t oamnbr = static_cast<uint8_t>(std::stoi(property_oamnbr.Text.empty() ? "0" : property_oamnbr.Text));
320 uint8_t hitbox = static_cast<uint8_t>(std::stoi(property_hitbox.Text.empty() ? "0" : property_hitbox.Text));
321 uint8_t health = static_cast<uint8_t>(std::stoi(property_health.Text.empty() ? "0" : property_health.Text));
322 uint8_t damage = static_cast<uint8_t>(std::stoi(property_damage.Text.empty() ? "0" : property_damage.Text));
323
324 fs.write(reinterpret_cast<const char*>(&prize), sizeof(uint8_t));
325 fs.write(reinterpret_cast<const char*>(&palette), sizeof(uint8_t));
326 fs.write(reinterpret_cast<const char*>(&oamnbr), sizeof(uint8_t));
327 fs.write(reinterpret_cast<const char*>(&hitbox), sizeof(uint8_t));
328 fs.write(reinterpret_cast<const char*>(&health), sizeof(uint8_t));
329 fs.write(reinterpret_cast<const char*>(&damage), sizeof(uint8_t));
330
331 // Write sprite name
333
334 // Write user routines
335 int32_t routine_count = static_cast<int32_t>(userRoutines.size());
336 fs.write(reinterpret_cast<const char*>(&routine_count), sizeof(int32_t));
337 for (const auto& routine : userRoutines) {
338 WriteDotNetString(fs, routine.name);
339 WriteDotNetString(fs, routine.code);
340 }
341
342 // Write sprite ID
344
345 fs.close();
346 return absl::OkStatus();
347 }
348
352 void Reset() {
353 sprName.clear();
354 animations.clear();
355 userRoutines.clear();
356 editor.Frames.clear();
357
358 // Reset boolean properties
366 property_fast.IsChecked = false;
379
380 // Reset text properties
381 property_sprname.Text.clear();
382 property_prize.Text = "0";
384 property_oamnbr.Text = "0";
385 property_hitbox.Text = "0";
386 property_health.Text = "0";
387 property_damage.Text = "0";
388 property_sprid.Text.clear();
389 }
390
391 std::string sprName;
392 std::vector<AnimationGroup> animations;
393 std::vector<UserRoutine> userRoutines;
395
396 // Boolean properties (20 total)
418
419 // Stat properties (6 total, stored as text)
426
428};
429
430} // namespace zsprite
431} // namespace editor
432} // namespace yaze
433
434#endif // YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
std::string ReadDotNetString(std::istream &is)
Read a .NET BinaryReader format string (7-bit encoded length prefix).
Definition zsprite.h:27
void WriteDotNetString(std::ostream &os, const std::string &str)
Write a .NET BinaryWriter format string (7-bit encoded length prefix).
Definition zsprite.h:48
std::vector< OamTile > Tiles
Definition zsprite.h:103
AnimationGroup(uint8_t fs, uint8_t fe, uint8_t fsp, std::string fn)
Definition zsprite.h:93
std::vector< OamTile > Tiles
Definition zsprite.h:107
OamTile(uint8_t x, uint8_t y, bool mx, bool my, uint16_t id, uint8_t pal, bool s, uint8_t p)
Definition zsprite.h:69
std::vector< Frame > Frames
Definition zsprite.h:120
std::vector< UserRoutine > user_routines
Definition zsprite.h:121
UserRoutine(std::string n, std::string c)
Definition zsprite.h:112
std::vector< UserRoutine > userRoutines
Definition zsprite.h:393
void Reset()
Reset all sprite data to defaults.
Definition zsprite.h:352
SpriteProperty property_persist
Definition zsprite.h:411
absl::Status Save(const std::string &filename)
Save a ZSM file to disk.
Definition zsprite.h:254
SpriteProperty property_hitbox
Definition zsprite.h:423
SpriteProperty property_isboss
Definition zsprite.h:410
SpriteProperty property_impervious
Definition zsprite.h:406
SpriteProperty property_collisionlayer
Definition zsprite.h:399
SpriteProperty property_prize
Definition zsprite.h:420
SpriteProperty property_imperviousarrow
Definition zsprite.h:407
SpriteProperty property_smallshadow
Definition zsprite.h:413
SpriteProperty property_blockable
Definition zsprite.h:397
SpriteProperty property_shadow
Definition zsprite.h:412
absl::Status Load(const std::string &filename)
Load a ZSM file from disk.
Definition zsprite.h:134
SpriteProperty property_deflectarrows
Definition zsprite.h:402
SpriteProperty property_customdeath
Definition zsprite.h:400
SpriteProperty property_imperviousmelee
Definition zsprite.h:408
SpriteProperty property_fast
Definition zsprite.h:404
SpriteProperty property_oamnbr
Definition zsprite.h:422
SpriteProperty property_interaction
Definition zsprite.h:409
SpriteProperty property_watersprite
Definition zsprite.h:416
SpriteProperty property_statue
Definition zsprite.h:415
SpriteProperty property_sprname
Definition zsprite.h:417
SpriteProperty property_damagesound
Definition zsprite.h:401
SpriteProperty property_damage
Definition zsprite.h:425
std::vector< AnimationGroup > animations
Definition zsprite.h:392
SpriteProperty property_palette
Definition zsprite.h:421
SpriteProperty property_harmless
Definition zsprite.h:405
SpriteProperty property_health
Definition zsprite.h:424
SpriteProperty property_canfall
Definition zsprite.h:398
SpriteProperty property_statis
Definition zsprite.h:414
SpriteProperty property_deflectprojectiles
Definition zsprite.h:403
SpriteProperty property_sprid
Definition zsprite.h:427