88using Definitions . ObjectModels . Graphics ;
99using Definitions . ObjectModels . Objects . Sound ;
1010using Definitions . ObjectModels . Types ;
11+ using System . Collections . Immutable ;
1112using System . ComponentModel . DataAnnotations ;
1213using System . Text ;
1314
@@ -559,18 +560,70 @@ public static void WriteVariableStream(Stream ms, LocoObject obj)
559560
560561 public static void WriteStringTable ( Stream ms , StringTable table )
561562 {
563+ const bool wantVanillaExact = false ;
564+ if ( wantVanillaExact )
565+ {
566+ WriteVanillaStringTable ( ms , table ) ;
567+ }
568+ else
569+ {
570+ WriteSimpleStringTable ( ms , table ) ;
571+ }
572+ }
573+
574+ // this is a byte-perfect method for recreating the vanilla objects (specifically Currency)
575+ // however it has unnecessary bytes when strings are missing in certain languages
576+ // but other strings in the table have values for that language
577+ static void WriteVanillaStringTable ( Stream ms , StringTable table )
578+ {
579+ //var languagesUsed = table.Table
580+ // .Select(x => x.Value)
581+ // .Select(x => x
582+ // .Where(str => !string.IsNullOrEmpty(str.Value))
583+ // .Select(str => str.Key))
584+ // .SelectMany(x => x)
585+ // .Distinct()
586+ // .ToImmutableHashSet();
587+
588+ List < LanguageId > languagesUsed = [ LanguageId . English_UK , LanguageId . English_US ] ;
589+
562590 foreach ( var ste in table . Table )
563591 {
564- foreach ( var language in ste . Value . Where ( str => ! string . IsNullOrEmpty ( str . Value ) ) ) // skip strings with empty content
592+ foreach ( var language in ste . Value )
565593 {
566- ms . WriteByte ( ( byte ) language . Key ) ;
594+ // skip strings with empty content
595+ if ( ! string . IsNullOrEmpty ( language . Value ) )
596+ {
597+ ms . WriteByte ( ( uint8_t ) language . Key ) ;
598+ ms . Write ( Encoding . Latin1 . GetBytes ( language . Value ) ) ;
599+ ms . WriteByte ( ( uint8_t ) '\0 ' ) ;
600+ }
601+ else if ( languagesUsed . Contains ( language . Key ) ) // but if the string is empty, AND its language has other valid strings, vanilla objects actually wrote these useless bytes
602+ {
603+ // vanilla currency objects do this!!!!
604+ ms . WriteByte ( ( uint8_t ) language . Key ) ;
605+ ms . WriteByte ( ( uint8_t ) '\0 ' ) ;
606+ }
607+ }
608+
609+ ms . WriteByte ( LocoConstants . Terminator ) ;
610+ }
611+ }
567612
568- var strBytes = Encoding . Latin1 . GetBytes ( language . Value ) ;
569- ms . Write ( strBytes , 0 , strBytes . Length ) ;
570- ms . WriteByte ( ( byte ) '\0 ' ) ;
613+ // this is a simplified and more-correct way to write the string table to bytes
614+ // it is perfectly compatible with vanilla loco, but doesn't produce byte-accurate objects
615+ static void WriteSimpleStringTable ( Stream ms , StringTable table )
616+ {
617+ foreach ( var ste in table . Table )
618+ {
619+ foreach ( var language in ste . Value . Where ( x => ! string . IsNullOrEmpty ( x . Value ) ) ) // skip strings with empty content
620+ {
621+ ms . WriteByte ( ( uint8_t ) language . Key ) ;
622+ ms . Write ( Encoding . Latin1 . GetBytes ( language . Value ) ) ;
623+ ms . WriteByte ( ( uint8_t ) '\0 ' ) ;
571624 }
572625
573- ms . WriteByte ( 0xff ) ;
626+ ms . WriteByte ( LocoConstants . Terminator ) ;
574627 }
575628 }
576629
@@ -593,7 +646,7 @@ public static void WriteImageTable(Stream ms, List<GraphicsElement> graphicsElem
593646 Offset = g1Element . Flags . HasFlag ( DatG1ElementFlags . DuplicatePrevious ) ? previousOffset : offsetBytesIntoImageData ,
594647 } ;
595648
596- offsetBytesIntoImageData += ( uint ) newElement . ImageData . Length ;
649+ offsetBytesIntoImageData += ( uint32_t ) newElement . ImageData . Length ;
597650 encoded . Add ( newElement ) ;
598651
599652 previousOffset = newElement . Offset ;
0 commit comments