z_mirror/fmm
git clone https://git.raiguard.me/z_mirror/fmm
bde6027
trunk
1package fmm 2 3import ( 4"bufio" 5"encoding/binary" 6"errors" 7"fmt" 8"io" 9"math" 10) 11 12type AllowedCommands uint8 13 14const ( 15AllowedCommandsInvalid AllowedCommands = iota 16AllowedCommandsTrue 17AllowedCommandsFalse 18AllowedCommandsAdminsOnly 19) 20 21type PropertyTree interface { 22ptree () 23} 24 25type ( 26PropertyTreeNone struct {} 27PropertyTreeBool bool 28PropertyTreeNumber float64 29PropertyTreeString string 30PropertyTreeList []PropertyTree 31PropertyTreeDict map [string ]PropertyTree 32PropertyTreeSignedInteger int64 33PropertyTreeUnsignedInteger uint64 34) 35 36func (self * PropertyTreeNone )ptree () {} 37func (self * PropertyTreeBool )ptree () {} 38func (self * PropertyTreeNumber )ptree () {} 39func (self * PropertyTreeString )ptree () {} 40func (self * PropertyTreeList )ptree () {} 41func (self * PropertyTreeDict )ptree () {} 42func (self * PropertyTreeSignedInteger )ptree () {} 43func (self * PropertyTreeUnsignedInteger )ptree () {} 44 45type ModSettings struct { 46MapVersion Version 47Settings PropertyTree 48} 49 50type DatReader struct { 51reader * bufio.Reader 52err error 53} 54 55func newDatReader (reader io.Reader )DatReader { 56return DatReader { 57reader :bufio .NewReader (reader ), 58 } 59} 60 61func (r * DatReader )Read (buf []byte ) (int ,error ) { 62if r .err != nil { 63return 0 ,nil 64 } 65return r .reader .Read (buf ) 66} 67 68func (r * DatReader )ReadBool ()bool { 69if r .err != nil { 70return false 71 } 72value := false 73if err := binary .Read (r ,binary .LittleEndian ,& value );err != nil { 74r .err = err 75 } 76return value 77} 78 79func (r * DatReader )ReadUint8 ()uint8 { 80if r .err != nil { 81return 0 82 } 83value ,err := r .reader .ReadByte () 84if err != nil { 85r .err = err 86return 0 87 } 88return uint8 (value ) 89} 90 91func (r * DatReader )ReadUint16 ()uint16 { 92if r .err != nil { 93return 0 94 } 95var value uint16 96if err := binary .Read (r ,binary .LittleEndian ,& value );err != nil { 97r .err = err 98 } 99return value 100} 101 102func (r * DatReader )ReadUint16Optimized ()uint16 { 103if r .err != nil { 104return 0 105 } 106first := r .ReadUint8 () 107if first < math .MaxUint8 { 108return uint16 (first ) 109 } 110return r .ReadUint16 () 111} 112 113func (r * DatReader )ReadUint32 ()uint32 { 114if r .err != nil { 115return 0 116 } 117var val uint32 118if err := binary .Read (r ,binary .LittleEndian ,& val );err != nil { 119r .err = err 120 } 121return val 122} 123 124func (r * DatReader )ReadUint32Optimized ()uint32 { 125if r .err != nil { 126return 0 127 } 128first := r .ReadUint8 () 129if first < math .MaxUint8 { 130return uint32 (first ) 131 } 132return r .ReadUint32 () 133} 134 135func (r * DatReader )ReadUint64 ()uint64 { 136if r .err != nil { 137return 0 138 } 139var val uint64 140if err := binary .Read (r ,binary .LittleEndian ,& val );err != nil { 141r .err = err 142 } 143return val 144} 145 146func (r * DatReader )ReadInt64 ()int64 { 147if r .err != nil { 148return 0 149 } 150var val int64 151if err := binary .Read (r ,binary .LittleEndian ,& val );err != nil { 152r .err = err 153 } 154return val 155} 156 157func (r * DatReader )ReadDouble ()float64 { 158if r .err != nil { 159return 0 160 } 161var val float64 162if err := binary .Read (r ,binary .LittleEndian ,& val );err != nil { 163r .err = err 164 } 165return val 166} 167 168func (r * DatReader )ReadString ()string { 169if r .err != nil { 170return "" 171 } 172length := r .ReadUint32Optimized () 173stringBuf := make ([]byte ,length ) 174_ ,err := io .ReadFull (r .reader ,stringBuf ) 175if err != nil { 176r .err = err 177 } 178return string (stringBuf ) 179} 180 181func (r * DatReader )ReadStringOptional ()string { 182if r .err != nil { 183return "" 184 } 185empty := r .ReadBool () 186if empty { 187return "" 188 } 189return r .ReadString () 190} 191 192func (r * DatReader )ReadVersionOptimized (withBuild bool )Version { 193if r .err != nil { 194return Version {} 195 } 196ver := Version { 197r .ReadUint16Optimized (), 198r .ReadUint16Optimized (), 199r .ReadUint16Optimized (), 200 } 201if withBuild { 202ver [3 ]= r .ReadUint16Optimized () 203 } 204return ver 205} 206 207func (r * DatReader )ReadVersionUnoptimized ()Version { 208if r .err != nil { 209return Version {} 210 } 211return Version { 212r .ReadUint16 (), 213r .ReadUint16 (), 214r .ReadUint16 (), 215r .ReadUint16 (), 216 } 217} 218 219func (r * DatReader )ReadModWithCRC ()ModIdent { 220if r .err != nil { 221return ModIdent {} 222 } 223name := r .ReadString () 224version := r .ReadVersionOptimized (false ) 225r .ReadUint32 ()// CRC 226return ModIdent {name ,& version } 227} 228 229func (r * DatReader )ReadPropertyTree ()PropertyTree { 230if r .err != nil { 231return & PropertyTreeNone {} 232 } 233kind := r .ReadUint8 () 234r .ReadBool ()// Internal flag that we do not care about 235switch kind { 236case 0 : 237return & PropertyTreeNone {} 238case 1 : 239return ptr (PropertyTreeBool (r .ReadBool ())) 240case 2 : 241return ptr (PropertyTreeNumber (r .ReadDouble ())) 242case 3 : 243return ptr (PropertyTreeString (r .ReadStringOptional ())) 244case 4 : 245length := r .ReadUint32 () 246res := []PropertyTree {} 247for i := uint32 (0 );i < length ;i ++ { 248r .ReadStringOptional () 249res = append (res ,r .ReadPropertyTree ()) 250 } 251return ptr (PropertyTreeList (res )) 252case 5 : 253length := r .ReadUint32 () 254res := map [string ]PropertyTree {} 255for i := uint32 (0 );i < length ;i ++ { 256res [r .ReadStringOptional ()]= r .ReadPropertyTree () 257 } 258return ptr (PropertyTreeDict (res )) 259case 6 : 260return ptr (PropertyTreeSignedInteger (r .ReadInt64 ())) 261case 7 : 262return ptr (PropertyTreeSignedInteger (r .ReadUint64 ())) 263 } 264 265r .err = errors .New (fmt .Sprintf ("unknown property tree kind: %d\n" ,kind )) 266return & PropertyTreeNone {} 267} 268 269func (r * DatReader )ReadModSettings ()ModSettings { 270if r .err != nil { 271return ModSettings {} 272 } 273mapVersion := r .ReadVersionUnoptimized () 274r .ReadBool ()// Internal flag, always false 275settings := r .ReadPropertyTree () 276return ModSettings {mapVersion ,settings } 277} 278 279func (r * DatReader )ReadAllowedCommands ()AllowedCommands { 280result := AllowedCommandsInvalid 281if r .err != nil { 282return result 283 } 284raw := r .ReadUint8 () 285switch raw { 286case 1 : 287result = AllowedCommandsTrue 288case 2 : 289result = AllowedCommandsFalse 290case 3 : 291result = AllowedCommandsAdminsOnly 292 } 293return result 294} 295 296type DatWriter struct { 297writer * bufio.Writer 298err error 299} 300 301func (w * DatWriter )Write (buf []byte ) (int ,error ) { 302if w .err != nil { 303return 0 ,nil 304 } 305return w .writer .Write (buf ) 306} 307 308func newDatWriter (writer io.Writer )DatWriter { 309return DatWriter { 310writer :bufio .NewWriter (writer ), 311 } 312} 313 314func (w * DatWriter )WriteBool (value bool ) { 315if w .err != nil { 316return 317 } 318binary .Write (w ,binary .LittleEndian ,value ) 319} 320 321func (w * DatWriter )WriteUint8 (value uint8 ) { 322if w .err != nil { 323return 324 } 325binary .Write (w ,binary .LittleEndian ,value ) 326} 327 328func (w * DatWriter )WriteUint16 (value uint16 ) { 329if w .err != nil { 330return 331 } 332binary .Write (w ,binary .LittleEndian ,value ) 333} 334 335func (w * DatWriter )WriteUint16Optimized (value uint16 ) { 336if w .err != nil { 337return 338 } 339if value < math .MaxUint8 { 340w .WriteUint8 (uint8 (value )) 341 }else { 342w .WriteUint16 (value ) 343 } 344} 345 346func (w * DatWriter )WriteUint32 (value uint32 ) { 347if w .err != nil { 348return 349 } 350binary .Write (w ,binary .LittleEndian ,value ) 351} 352 353func (w * DatWriter )WriteUint32Optimized (value uint32 ) { 354if w .err != nil { 355return 356 } 357if value < math .MaxUint8 { 358w .WriteUint8 (uint8 (value )) 359 }else { 360w .WriteUint32 (value ) 361 } 362} 363 364func (w * DatWriter )WriteUint64 (value uint64 ) { 365if w .err != nil { 366return 367 } 368binary .Write (w ,binary .LittleEndian ,value ) 369} 370 371func (w * DatWriter )WriteInt64 (value int64 ) { 372if w .err != nil { 373return 374 } 375binary .Write (w ,binary .LittleEndian ,value ) 376} 377 378func (w * DatWriter )WriteDouble (value float64 ) { 379if w .err != nil { 380return 381 } 382binary .Write (w ,binary .LittleEndian ,value ) 383} 384 385func (w * DatWriter )WriteString (value string ) { 386if w .err != nil { 387return 388 } 389length := len (value ) 390if length > math .MaxUint32 { 391w .err = errors .New (fmt .Sprintf ("PropertyTree string is too long: %d" ,length )) 392return 393 } 394w .WriteUint32Optimized (uint32 (length )) 395_ ,err := w .writer .WriteString (value ) 396if err != nil { 397w .err = err 398 } 399} 400 401func (w * DatWriter )WriteStringOptional (value string ) { 402if w .err != nil { 403return 404 } 405if value == "" { 406w .WriteBool (true ) 407return 408 } 409w .WriteBool (false ) 410w .WriteString (value ) 411} 412 413func (w * DatWriter )WriteVersionUnoptimized (version Version ) { 414if w .err != nil { 415return 416 } 417w .WriteUint16 (version [0 ]) 418w .WriteUint16 (version [1 ]) 419w .WriteUint16 (version [2 ]) 420w .WriteUint16 (version [3 ]) 421} 422 423func (w * DatWriter )WritePropertyTree (pt PropertyTree ) { 424if w .err != nil { 425return 426 } 427switch val := pt .(type ) { 428case * PropertyTreeNone : 429w .WriteUint8 (0 )// Type 430w .WriteUint8 (0 )// Unused internal flag 431case * PropertyTreeBool : 432w .WriteUint8 (1 )// Type 433w .WriteUint8 (0 )// Unused internal flag 434w .WriteBool (bool (* val )) 435case * PropertyTreeNumber : 436w .WriteUint8 (2 )// Type 437w .WriteUint8 (0 )// Unused internal flag 438w .WriteDouble (float64 (* val )) 439case * PropertyTreeString : 440w .WriteUint8 (3 )// Type 441w .WriteUint8 (0 )// Unused internal flag 442w .WriteStringOptional (string (* val )) 443case * PropertyTreeList : 444w .WriteUint8 (4 )// Type 445w .WriteUint8 (0 )// Unused internal flag 446list := []PropertyTree (* val ) 447w .WriteUint32 (uint32 (len (list ))) 448for _ ,child := range list { 449w .WriteStringOptional ("" ) 450w .WritePropertyTree (child ) 451 } 452case * PropertyTreeDict : 453w .WriteUint8 (5 )// Type 454w .WriteUint8 (0 )// Unused internal flag 455dict := map [string ]PropertyTree (* val ) 456w .WriteUint32 (uint32 (len (dict ))) 457for key ,child := range dict { 458w .WriteStringOptional (key ) 459w .WritePropertyTree (child ) 460 } 461case * PropertyTreeSignedInteger : 462w .WriteUint8 (6 )// Type 463w .WriteUint8 (0 )// Unused internal flag 464w .WriteInt64 (int64 (* val )) 465case * PropertyTreeUnsignedInteger : 466w .WriteUint8 (7 )// Type 467w .WriteUint8 (0 )// Unused internal flag 468w .WriteUint64 (uint64 (* val )) 469 } 470} 471 472func (w * DatWriter )WriteModSettings (input * ModSettings ) { 473if w .err != nil { 474return 475 } 476w .WriteVersionUnoptimized (input .MapVersion ) 477w .WriteBool (false )// Internal flag, always false 478w .WritePropertyTree (input .Settings ) 479} 480 481func (w * DatWriter )Flush () { 482if w .err != nil { 483return 484 } 485w .err = w .writer .Flush () 486}