1 #include <stdio.h>
2 
3 #include <list>
4 #include <vector>
5 #include <map>
6 #include <utility>
7 #include <bitset>
8 #include <stdint.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <iterator>
12 #include <algorithm>
13 
14 #include "locutus/locutus_types.hpp"
15 
16 extern "C"
17 {
18 #   include "misc/ll.h"
19 #   include "misc/types.h"
20 #   include "metadata/metadata.h"
21 #   include "metadata/cfgvar_metadata.h"
22 }
23 
24 // These need to match LUIGI specification
25 enum : uint8_t
26 {
27     METATAG_NAME        = 0x00,
28     METATAG_SHORT_NAME  = 0x01,
29     METATAG_AUTHOR      = 0x02,
30     METATAG_PUBLISHER   = 0x03,
31     METATAG_RLS_DATE    = 0x04,
32     METATAG_LICENSE     = 0x05,
33     METATAG_DESCRIPTION = 0x06,
34     METATAG_MISC        = 0x07,
35     METATAG_GAME_ARTIST = 0x08,
36     METATAG_MUSIC_BY    = 0x09,
37     METATAG_SFX_BY      = 0x0A,
38     METATAG_VOICES_BY   = 0x0B,
39     METATAG_DOCS_BY     = 0x0C,
40     METATAG_CONCEPT_BY  = 0x0D,
41     METATAG_BOX_ARTIST  = 0x0E,
42     METATAG_MORE_INFO   = 0x0F
43 };
44 
45 // ------------------------------------------------------------------------ //
46 //  STRING_VEC_TO_ARRAY                                                     //
47 // ------------------------------------------------------------------------ //
string_vec_to_array(const t_string_vec & vec)48 LOCAL const char **string_vec_to_array( const t_string_vec& vec )
49 {
50     if ( !vec.size() ) return nullptr;
51 
52     const char **array =
53         static_cast<const char**>(calloc( sizeof(char *), vec.size() + 1));
54 
55     if ( !array )
56         return nullptr;
57 
58     int idx = 0;
59 
60     for ( const auto& str : vec )
61         array[idx++] = strdup( str.c_str() );
62 
63     return array;
64 }
65 
66 // ------------------------------------------------------------------------ //
67 //  GAME_DATE_VEC_TO_ARRAY                                                  //
68 // ------------------------------------------------------------------------ //
game_date_vec_to_array(const t_game_date_vec & vec)69 LOCAL game_date_t *game_date_vec_to_array( const t_game_date_vec& vec )
70 {
71     if ( !vec.size() ) return nullptr;
72 
73     game_date_t *array =
74         static_cast<game_date_t *>(calloc(sizeof(game_date_t), vec.size() + 1));
75 
76     if ( !array )
77         return nullptr;
78 
79     int idx = 0;
80 
81     for ( const auto& game_date : vec )
82         array[idx++] = game_date;
83 
84     return array;
85 }
86 
87 // ------------------------------------------------------------------------ //
88 //  STRING_ARRAY_TO_VEC                                                     //
89 // ------------------------------------------------------------------------ //
string_array_to_vec(const char ** array)90 LOCAL t_string_vec string_array_to_vec( const char **array )
91 {
92     t_string_vec vec;
93 
94     if ( array )
95     {
96         while ( *array )
97             vec.push_back( *array++ );
98     }
99 
100     return vec;
101 }
102 
103 // ------------------------------------------------------------------------ //
104 //  GAME_DATE_ARRAY_TO_VEC                                                  //
105 // ------------------------------------------------------------------------ //
game_date_array_to_vec(const game_date_t * array)106 LOCAL t_game_date_vec game_date_array_to_vec( const game_date_t* array )
107 {
108     t_game_date_vec vec;
109 
110     if ( array )
111     {
112         while ( array->year )
113             vec.push_back( *array++ );
114     }
115 
116     return vec;
117 }
118 
119 // ------------------------------------------------------------------------ //
120 //  T_METADATA::PACK_STRING                                                 //
121 // ------------------------------------------------------------------------ //
pack_string(uint8_t tag,t_byte_vec & v,const std::string & s) const122 void t_metadata::pack_string( uint8_t tag, t_byte_vec& v,
123                               const std::string& s ) const
124 {
125     auto si = s.cbegin();
126     auto se = s.cend();
127     if ( std::distance( si, se ) > 255 )
128         se = si + 255;
129 
130     const auto len =  std::distance( si, se );
131     v.reserve( v.size() + len + 2 );
132     v.push_back( tag );
133     v.push_back( uint8_t( len ) );
134     std::copy( si, se, std::back_inserter( v ) );
135 }
136 
137 // ------------------------------------------------------------------------ //
138 //  T_METADATA::UNPACK_STRING                                               //
139 // ------------------------------------------------------------------------ //
140 template <typename T>
unpack_string(std::string & str,T & si,uint8_t len)141 void t_metadata::unpack_string( std::string& str, T& si, uint8_t len )
142 {
143     str.reserve( len );
144     std::copy( si, si + len, std::back_inserter( str ) );
145 
146     si += len;
147 }
148 
149 template <typename T>
unpack_string(t_string_vec & vec,T & si,uint8_t len)150 void t_metadata::unpack_string( t_string_vec& vec, T& si, uint8_t len )
151 {
152     vec.push_back( std::string("") );
153     unpack_string( vec.back(), si, len );
154 }
155 
156 // ------------------------------------------------------------------------ //
157 //  T_METADATA::EMPTY                                                       //
158 // ------------------------------------------------------------------------ //
empty() const159 bool t_metadata::empty( ) const
160 {
161     return name.empty()                 &&
162            short_name.empty()           &&
163            authors.empty()              &&
164            game_artists.empty()         &&
165            composers.empty()            &&
166            sfx_artists.empty()          &&
167            voice_actors.empty()         &&
168            doc_writers.empty()          &&
169            conceptualizers.empty()      &&
170            box_artists.empty()          &&
171            more_infos.empty()           &&
172            publishers.empty()           &&
173            release_dates.empty()        &&
174            licenses.empty()             &&
175            descriptions.empty()         &&
176            misc.empty();
177 }
178 
179 // ------------------------------------------------------------------------ //
180 //  T_METADATA::CLEAR                                                       //
181 // ------------------------------------------------------------------------ //
clear()182 void t_metadata::clear()
183 {
184     name.clear();
185     short_name.clear();
186     authors.clear();
187     game_artists.clear();
188     composers.clear();
189     sfx_artists.clear();
190     voice_actors.clear();
191     doc_writers.clear();
192     conceptualizers.clear();
193     box_artists.clear();
194     more_infos.clear();
195     publishers.clear();
196     release_dates.clear();
197     licenses.clear();
198     descriptions.clear();
199     misc.clear();
200 }
201 
202 // ------------------------------------------------------------------------ //
203 //  T_METADATA::SERIALIZE                                                   //
204 // ------------------------------------------------------------------------ //
serialize() const205 t_byte_vec t_metadata::serialize( ) const
206 {
207     t_byte_vec serial;
208 
209     if ( name.length() > 0 )
210         pack_string( METATAG_NAME, serial, name );
211 
212     if ( short_name.length() > 0 )
213         pack_string( METATAG_SHORT_NAME, serial, short_name );
214 
215     for ( const auto& author : authors )
216         pack_string( METATAG_AUTHOR, serial, author );
217 
218     for ( const auto& name_credit : game_artists )
219         pack_string( METATAG_GAME_ARTIST, serial, name_credit );
220 
221     for ( const auto& name_credit : composers )
222         pack_string( METATAG_MUSIC_BY, serial, name_credit );
223 
224     for ( const auto& name_credit : sfx_artists )
225         pack_string( METATAG_SFX_BY, serial, name_credit );
226 
227     for ( const auto& name_credit : voice_actors )
228         pack_string( METATAG_VOICES_BY, serial, name_credit );
229 
230     for ( const auto& name_credit : doc_writers )
231         pack_string( METATAG_DOCS_BY, serial, name_credit );
232 
233     for ( const auto& name_credit : conceptualizers )
234         pack_string( METATAG_CONCEPT_BY, serial, name_credit );
235 
236     for ( const auto& name_credit : box_artists )
237         pack_string( METATAG_BOX_ARTIST, serial, name_credit );
238 
239     for ( const auto& info : more_infos )
240         pack_string( METATAG_MORE_INFO, serial, info );
241 
242     for ( const auto& publisher : publishers )
243         pack_string( METATAG_PUBLISHER, serial, publisher );
244 
245     if ( release_dates.size() )
246     {
247         /* Reserve worst-case storage. */
248         serial.reserve( serial.size() + release_dates.size() * 10 );
249 
250         for ( const auto& date : release_dates )
251         {
252             if ( !date.year )
253                 continue;
254 
255             serial.push_back( METATAG_RLS_DATE );
256 
257             uint8_t data[8];
258             const auto length = game_date_to_uint8_t(&date, data);
259             serial.push_back( length & 0xFF );
260 
261             for (auto i = 0; i < length; i++)
262                 serial.push_back(data[i]);
263         }
264     }
265 
266     for ( const auto& license : licenses )
267         pack_string( METATAG_LICENSE, serial, license );
268 
269     for ( const auto& description : descriptions )
270         pack_string( METATAG_DESCRIPTION, serial, description );
271 
272     for ( const auto& item : misc )
273         pack_string( METATAG_MISC, serial, item );
274 
275     return serial;
276 }
277 
278 // ------------------------------------------------------------------------ //
279 //  T_METADATA::DESERIALIZE                                                 //
280 // ------------------------------------------------------------------------ //
deserialize(const t_byte_vec & serial)281 void t_metadata::deserialize( const t_byte_vec& serial )
282 {
283 
284     clear();
285 
286     auto si = serial.cbegin();
287     auto se = serial.cend();
288 
289     while ( si != se )
290     {
291         const auto remain = std::distance( si, se );
292 
293         assert( remain >= 2);
294 
295         const uint8_t tag = *si++;
296         const uint8_t len = *si++;
297 
298         assert( remain >= 2 + len );
299 
300         switch ( tag )
301         {
302             case METATAG_NAME:
303                 unpack_string( name, si, len );
304                 break;
305             case METATAG_SHORT_NAME:
306                 unpack_string( short_name, si, len );
307                 break;
308             case METATAG_AUTHOR:
309                 unpack_string( authors, si, len );
310                 break;
311             case METATAG_GAME_ARTIST:
312                 unpack_string( game_artists, si, len );
313                 break;
314             case METATAG_MUSIC_BY:
315                 unpack_string( composers, si, len );
316                 break;
317             case METATAG_SFX_BY:
318                 unpack_string( sfx_artists, si, len );
319                 break;
320             case METATAG_VOICES_BY:
321                 unpack_string( voice_actors, si, len );
322                 break;
323             case METATAG_DOCS_BY:
324                 unpack_string( doc_writers, si, len );
325                 break;
326             case METATAG_CONCEPT_BY:
327                 unpack_string( conceptualizers, si, len );
328                 break;
329             case METATAG_BOX_ARTIST:
330                 unpack_string( box_artists, si, len );
331                 break;
332             case METATAG_MORE_INFO:
333                 unpack_string( more_infos, si, len );
334                 break;
335             case METATAG_PUBLISHER:
336                 unpack_string( publishers, si, len );
337                 break;
338             case METATAG_RLS_DATE:
339             {
340                 const auto data = &*si;
341                 game_date_t game_date;
342 
343                 if (uint8_t_to_game_date(&game_date, data, len) == 0)
344                     release_dates.push_back( game_date );
345 
346                 si += len;
347                 break;
348             }
349             case METATAG_LICENSE:
350                 unpack_string( licenses, si, len );
351                 break;
352             case METATAG_DESCRIPTION:
353                 unpack_string( descriptions, si, len );
354                 break;
355             case METATAG_MISC:
356                 unpack_string( misc, si, len );
357                 break;
358             default:
359                 // Silently skip unknown tags
360                 si += len;
361         }
362     }
363 }
364 
365 // ------------------------------------------------------------------------ //
366 //  T_METADATA::FROM_GAME_METADATA                                          //
367 //                                                                          //
368 //  TODO:  Rewrite the code to not need this at all, and just hold a        //
369 //         pointer to the game_metadata structure.  Requires rewriting all  //
370 //         the serialization/deserialization code, though.                  //
371 // ------------------------------------------------------------------------ //
from_game_metadata(const game_metadata_t * game_metadata)372 void t_metadata::from_game_metadata( const game_metadata_t* game_metadata )
373 {
374     clear();
375 
376     if ( game_metadata->name )
377         name = game_metadata->name;
378 
379     if ( game_metadata->short_name )
380         short_name = game_metadata->short_name;
381 
382     authors         = string_array_to_vec( game_metadata->authors           );
383     game_artists    = string_array_to_vec( game_metadata->game_artists      );
384     composers       = string_array_to_vec( game_metadata->composers         );
385     sfx_artists     = string_array_to_vec( game_metadata->sfx_artists       );
386     voice_actors    = string_array_to_vec( game_metadata->voice_actors      );
387     doc_writers     = string_array_to_vec( game_metadata->doc_writers       );
388     conceptualizers = string_array_to_vec( game_metadata->conceptualizers   );
389     box_artists     = string_array_to_vec( game_metadata->box_artists       );
390     more_infos      = string_array_to_vec( game_metadata->more_infos        );
391     release_dates   = game_date_array_to_vec( game_metadata->release_dates  );
392     publishers      = string_array_to_vec( game_metadata->publishers        );
393     licenses        = string_array_to_vec( game_metadata->licenses          );
394     descriptions    = string_array_to_vec( game_metadata->descriptions      );
395     misc            = string_array_to_vec( game_metadata->misc              );
396 
397     ecs_compat      = game_metadata->ecs_compat;
398     voice_compat    = game_metadata->voice_compat;
399     intv2_compat    = game_metadata->intv2_compat;
400     kc_compat       = game_metadata->kc_compat;
401     tv_compat       = game_metadata->tv_compat;
402     lto_mapper      = game_metadata->lto_mapper;
403     jlp_accel       = game_metadata->jlp_accel;
404     jlp_flash       = game_metadata->jlp_flash;
405     is_defaults     = game_metadata->is_defaults;
406 
407     // -------------------------------------------------------------------- //
408     //  Ugly:  LUIGI doesn't have explicit tags for build_date or version,  //
409     //  so for now pack these as 'misc' tags.  That's fine for a rom2luigi  //
410     //  then luigi2bin round-trip; however, ultimately these should become  //
411     //  tags.  Unfortunately, the early versions of the LUIGI decoder will  //
412     //  abort() if they see a tag they don't understand.                    //
413     // -------------------------------------------------------------------- //
414     if (game_metadata->build_dates)
415     {
416         for (auto i = 0; game_metadata->build_dates[i].year; ++i)
417         {
418             char *dstr = game_date_to_string(&game_metadata->build_dates[i]);
419             misc.push_back(std::string("build_date=") + dstr);
420             free(dstr);
421         }
422     }
423 
424     if (game_metadata->versions)
425         for (auto i = 0; game_metadata->versions[i]; ++i)
426             misc.push_back(std::string("version=") +
427                            game_metadata->versions[i]);
428 }
429 
430 // ------------------------------------------------------------------------ //
431 //  T_METADATA::TO_GAME_METADATA                                            //
432 // ------------------------------------------------------------------------ //
to_game_metadata() const433 game_metadata_t *t_metadata::to_game_metadata( ) const
434 {
435     game_metadata_t *game_metadata =
436         static_cast<game_metadata_t *>(calloc( sizeof(game_metadata_t), 1 ));
437 
438     if ( !game_metadata )
439         return nullptr;
440 
441     if ( name.length() )
442         game_metadata->name = strdup( name.c_str() );
443 
444     if ( short_name.length() )
445         game_metadata->short_name = strdup( short_name.c_str() );
446 
447     game_metadata->authors         = string_vec_to_array( authors           );
448     game_metadata->game_artists    = string_vec_to_array( game_artists      );
449     game_metadata->composers       = string_vec_to_array( composers         );
450     game_metadata->sfx_artists     = string_vec_to_array( sfx_artists       );
451     game_metadata->voice_actors    = string_vec_to_array( voice_actors      );
452     game_metadata->doc_writers     = string_vec_to_array( doc_writers       );
453     game_metadata->conceptualizers = string_vec_to_array( conceptualizers   );
454     game_metadata->box_artists     = string_vec_to_array( box_artists       );
455     game_metadata->more_infos      = string_vec_to_array( more_infos        );
456     game_metadata->release_dates   = game_date_vec_to_array( release_dates  );
457     game_metadata->publishers      = string_vec_to_array( publishers        );
458     game_metadata->licenses        = string_vec_to_array( licenses          );
459     game_metadata->descriptions    = string_vec_to_array( descriptions      );
460     game_metadata->misc            = nullptr;
461 
462     game_metadata->ecs_compat      = ecs_compat;
463     game_metadata->voice_compat    = voice_compat;
464     game_metadata->intv2_compat    = intv2_compat;
465     game_metadata->kc_compat       = kc_compat;
466     game_metadata->tv_compat       = tv_compat;
467     game_metadata->lto_mapper      = lto_mapper;
468     game_metadata->jlp_accel       = jlp_accel;
469     game_metadata->jlp_flash       = jlp_flash;
470     game_metadata->is_defaults     = is_defaults;
471 
472     // Pull apart 'misc' array into a cfg_var_t list that we can run through
473     // the cfgvar_metadata infrastructure.  Merge any metadata that results
474     // with our partial metadata above.
475     if ( misc.size() )
476     {
477         cfg_var_t *cv = nullptr;
478 
479         for ( const auto& misc_str : misc )
480             append_cfg_var( &cv, cons_cfg_var_kv_str( misc_str.c_str() ) );
481 
482         auto misc_gm = game_metadata_from_cfgvars( cv );
483 
484         if ( misc_gm )
485         {
486             auto merged_gm = merge_game_metadata( game_metadata, misc_gm );
487             if ( merged_gm )
488             {
489                 free_game_metadata( game_metadata );
490                 game_metadata = merged_gm;
491             }
492             free_game_metadata( misc_gm );
493         }
494 
495         free_cfg_var_list( cv );
496     }
497 
498     return game_metadata;
499 }
500