@@ -24,6 +24,7 @@ class Program
2424 private static readonly HashSet < uint > _ignoreIds = new HashSet < uint > ( ) ;
2525
2626 private static readonly Dictionary < uint , uint > _replaceIds = new Dictionary < uint , uint > ( ) ;
27+ private static readonly Dictionary < ushort , ushort > _clientToServerIds = new Dictionary < ushort , ushort > ( ) ;
2728
2829 private static Client _client ;
2930
@@ -41,6 +42,7 @@ class Program
4142 private static Logger . LogOutput _logOutput = Logger . LogOutput . Console ;
4243
4344 private static string _currentFilename ;
45+ private static string _otbFilename ;
4446 private static string _outDirectory ;
4547 private static string _recording ;
4648 private static string _tibiaDirectory = string . Empty ;
@@ -104,6 +106,10 @@ static bool ParseArgs(string[] args)
104106 Console . WriteLine ( "[optional] --time=<seconds> or --timestamp=<seconds>: " +
105107 "<seconds> is the number of seconds from the start of the recording to stop extraction." +
106108 "If this is not specified, extraction will run until the end of the recording." ) ;
109+ Console . WriteLine ( "[optional] --otb=<path>: " +
110+ "<path> is the path to your items.otb file. +" +
111+ "By default, the OTBM file is created using client IDs for items. +" +
112+ "By specifying an OTB file, the OTBM file will be created with server IDs." ) ;
107113
108114 Console . WriteLine ( "[optional] --loglevel=[debug,info,warning,error,disabled]: " +
109115 "Sets the log level within the API. Default: error" ) ;
@@ -157,6 +163,11 @@ static bool ParseArgs(string[] args)
157163 }
158164 }
159165 break ;
166+ case "--otb" :
167+ {
168+ _otbFilename = splitArg [ 1 ] . Replace ( "\" " , "" ) ;
169+ }
170+ break ;
160171 case "--loglevel" :
161172 {
162173 _logLevel = Logger . ConvertToLogLevel ( splitArg [ 1 ] ) ;
@@ -235,6 +246,11 @@ static void Main(string[] args)
235246 }
236247 }
237248
249+ if ( ! string . IsNullOrEmpty ( _otbFilename ) )
250+ {
251+ LoadOtb ( ) ;
252+ }
253+
238254 if ( _extractMapData )
239255 {
240256 LoadXML ( "ItemsIgnore.xml" ) ;
@@ -295,6 +311,118 @@ static void Main(string[] args)
295311 }
296312 }
297313
314+ private static void LoadOtb ( )
315+ {
316+ Console . WriteLine ( "Loading OTB file..." ) ;
317+ using var fileStream = new BinaryReader ( File . OpenRead ( _otbFilename ) ) ;
318+ while ( fileStream . ReadByte ( ) != 0xFE ) { } // skip to root node
319+ while ( fileStream . ReadByte ( ) != 0x01 ) { } // skip to version attribute
320+ var skipBytes = fileStream . ReadUInt16 ( ) ;
321+ fileStream . BaseStream . Seek ( skipBytes , SeekOrigin . Current ) ; // skip version info
322+ while ( fileStream . BaseStream . Position < fileStream . BaseStream . Length )
323+ {
324+ var serverId = ushort . MinValue ;
325+ var clientId = ushort . MinValue ;
326+
327+ // We've reached the end of the file.
328+ if ( fileStream . ReadByte ( ) == 0xFF )
329+ {
330+ break ;
331+ }
332+
333+ // The OTB format is really, really stupid (no offense).
334+ using var ms = new MemoryStream ( ) ;
335+ while ( true )
336+ {
337+ var value = fileStream . ReadByte ( ) ;
338+ if ( value == 0xFE || value == 0xFF )
339+ {
340+ break ;
341+ }
342+ else if ( value == 0xFD )
343+ {
344+ value = fileStream . ReadByte ( ) ;
345+ }
346+ ms . WriteByte ( value ) ;
347+ }
348+
349+ ms . Position = 0 ;
350+ using var bs = new BinaryReader ( ms ) ;
351+
352+ bs . ReadByte ( ) ; // item group
353+ bs . ReadUInt32 ( ) ; // item flags
354+
355+ while ( bs . BaseStream . Position < bs . BaseStream . Length )
356+ {
357+ var attribute = bs . ReadByte ( ) ;
358+ var dataLen = bs . ReadUInt16 ( ) ;
359+ if ( attribute == 0x10 )
360+ {
361+ serverId = bs . ReadUInt16 ( ) ;
362+ }
363+ else if ( attribute == 0x11 )
364+ {
365+ clientId = bs . ReadUInt16 ( ) ;
366+ }
367+ else
368+ {
369+ bs . BaseStream . Seek ( dataLen , SeekOrigin . Current ) ;
370+ }
371+
372+ if ( serverId != 0 && clientId != 0 )
373+ {
374+ if ( _clientToServerIds . ContainsKey ( clientId ) )
375+ {
376+ var value = _clientToServerIds [ clientId ] ;
377+ Console . WriteLine ( $ "Failed to map Server ID `{ serverId } ` to Client ID `{ clientId } `. " +
378+ $ "Client ID `{ clientId } ` is already mapped to Server ID `{ value } `.") ;
379+ }
380+ else
381+ {
382+ _clientToServerIds [ clientId ] = serverId ;
383+ }
384+ serverId = 0 ;
385+ clientId = 0 ;
386+ }
387+ }
388+ }
389+ Console . WriteLine ( "Done" ) ;
390+ }
391+
392+ static void LoadXML ( string filename )
393+ {
394+ using ( var file = File . OpenRead ( filename ) )
395+ {
396+ Console . WriteLine ( $ "Loading { filename } ...") ;
397+ using ( var reader = XmlReader . Create ( file ) )
398+ {
399+ while ( reader . Read ( ) )
400+ {
401+ if ( ! reader . IsStartElement ( ) )
402+ {
403+ continue ;
404+ }
405+
406+ if ( ! reader . Name . Equals ( "item" , StringComparison . CurrentCultureIgnoreCase ) )
407+ {
408+ continue ;
409+ }
410+
411+ if ( uint . TryParse ( reader [ "id" ] , out var id ) )
412+ {
413+ _ignoreIds . Add ( id ) ;
414+ }
415+ else if ( uint . TryParse ( reader [ "fromid" ] , out var fromId ) &&
416+ uint . TryParse ( reader [ "toid" ] , out var toId ) )
417+ {
418+ _replaceIds . Add ( fromId , toId ) ;
419+ }
420+ }
421+ }
422+ Console . WriteLine ( "Done" ) ;
423+ }
424+ }
425+
298426 private static void ExtractRecordings ( List < string > filenames )
299427 {
300428 Console . WriteLine ( $ "Extracting data from { filenames . Count } recording(s)...") ;
@@ -671,41 +799,6 @@ static bool Connection_OnReceivedMapPacket(Packet packet)
671799 return true ;
672800 }
673801
674-
675- static void LoadXML ( string filename )
676- {
677- using ( var file = File . OpenRead ( filename ) )
678- {
679- Console . WriteLine ( $ "Loading { filename } ...") ;
680- using ( var reader = XmlReader . Create ( file ) )
681- {
682- while ( reader . Read ( ) )
683- {
684- if ( ! reader . IsStartElement ( ) )
685- {
686- continue ;
687- }
688-
689- if ( ! reader . Name . Equals ( "item" , StringComparison . CurrentCultureIgnoreCase ) )
690- {
691- continue ;
692- }
693-
694- if ( uint . TryParse ( reader [ "id" ] , out var id ) )
695- {
696- _ignoreIds . Add ( id ) ;
697- }
698- else if ( uint . TryParse ( reader [ "fromid" ] , out var fromId ) &&
699- uint . TryParse ( reader [ "toid" ] , out var toId ) )
700- {
701- _replaceIds . Add ( fromId , toId ) ;
702- }
703- }
704- }
705- Console . WriteLine ( "Done" ) ;
706- }
707- }
708-
709802 static FileStream InitializeMapFile ( string filename , string outputPath )
710803 {
711804 var file = new FileStream ( outputPath , FileMode . CreateNew , FileAccess . ReadWrite , FileShare . ReadWrite , 4096 , true ) ;
@@ -818,6 +911,10 @@ static void ParseField(Position position)
818911 {
819912 id = item . Id ;
820913 }
914+ if ( _clientToServerIds . TryGetValue ( ( ushort ) id , out var serverId ) )
915+ {
916+ id = serverId ;
917+ }
821918 WriteData ( _otbmFile , BitConverter . GetBytes ( ( ushort ) id ) ) ;
822919
823920 // item data
0 commit comments