@@ -16,6 +16,7 @@ namespace
1616constexpr char kOzzxMagic [8 ] = { ' O' , ' Z' , ' Z' , ' X' , ' P' , ' A' , ' C' , ' K' };
1717constexpr char kBoneMetadataMagic [4 ] = { ' B' , ' M' , ' D' , ' T' };
1818constexpr std::uint32_t kBoneMetadataVersion = 1u ;
19+ constexpr char kUserDataMagic [4 ] = { ' U' , ' D' , ' T' , ' A' };
1920}
2021
2122static bool ReadFully (std::ifstream& stream, void * buffer, std::size_t size)
@@ -193,6 +194,59 @@ static bool WriteBoneMetadataBlock(std::ofstream& stream, const ExtendedBoneMeta
193194 return true ;
194195}
195196
197+ static bool ReadUserDataBlock (std::ifstream& stream, std::vector<std::uint8_t >& out_user_data)
198+ {
199+ const std::streampos checkpoint = stream.tellg ();
200+
201+ char magic[sizeof (kUserDataMagic )] = {};
202+ if (!ReadFully (stream, magic, sizeof (magic)))
203+ {
204+ stream.clear ();
205+ stream.seekg (checkpoint, std::ios::beg);
206+ out_user_data.clear ();
207+ return true ;
208+ }
209+
210+ if (std::memcmp (magic, kUserDataMagic , sizeof (kUserDataMagic )) != 0 )
211+ {
212+ stream.seekg (checkpoint, std::ios::beg);
213+ out_user_data.clear ();
214+ return true ;
215+ }
216+
217+ std::uint32_t data_size = 0 ;
218+ if (!ReadFully (stream, &data_size, sizeof (data_size)))
219+ return false ;
220+
221+ out_user_data.clear ();
222+ out_user_data.resize (data_size);
223+ if (data_size > 0 && !ReadFully (stream, out_user_data.data (), data_size))
224+ return false ;
225+
226+ return true ;
227+ }
228+
229+ static bool WriteUserDataBlock (std::ofstream& stream, const std::vector<std::uint8_t >& user_data)
230+ {
231+ if (user_data.size () > static_cast <std::size_t >(std::numeric_limits<std::uint32_t >::max ()))
232+ {
233+ std::cerr << " User data payload too large for .ozzx bundle: " << user_data.size () << std::endl;
234+ return false ;
235+ }
236+
237+ if (!WriteFully (stream, kUserDataMagic , sizeof (kUserDataMagic )))
238+ return false ;
239+
240+ const std::uint32_t data_size = static_cast <std::uint32_t >(user_data.size ());
241+ if (!WriteFully (stream, &data_size, sizeof (data_size)))
242+ return false ;
243+
244+ if (data_size > 0 && !WriteFully (stream, user_data.data (), user_data.size ()))
245+ return false ;
246+
247+ return true ;
248+ }
249+
196250bool ReadOzzxBundle (const std::filesystem::path& path, OzzxBundle& out_bundle)
197251{
198252 std::ifstream stream (path, std::ios::binary);
@@ -249,6 +303,12 @@ bool ReadOzzxBundle(const std::filesystem::path& path, OzzxBundle& out_bundle)
249303 return false ;
250304 }
251305
306+ if (!ReadUserDataBlock (stream, out_bundle.user_data ))
307+ {
308+ std::cerr << " Failed to read user data block from: " << path << std::endl;
309+ return false ;
310+ }
311+
252312 out_bundle.motion_refs .clear ();
253313
254314 std::uint32_t motion_ref_count = 0 ;
@@ -352,6 +412,12 @@ bool WriteOzzxBundle(const std::filesystem::path& path, const OzzxBundle& bundle
352412 return false ;
353413 }
354414
415+ if (!WriteUserDataBlock (stream, bundle.user_data ))
416+ {
417+ std::cerr << " Failed to write user data block: " << path << std::endl;
418+ return false ;
419+ }
420+
355421 if (!WriteFully (stream, &motion_ref_count, sizeof (motion_ref_count)))
356422 {
357423 std::cerr << " Failed to write motion reference count: " << path << std::endl;
0 commit comments