github.com/df-mc/dragonfly@v0.9.13/server/world/mcdb/leveldat/level_dat.go (about) 1 package leveldat 2 3 import ( 4 "bufio" 5 "encoding/binary" 6 "fmt" 7 "github.com/sandertv/gophertunnel/minecraft/nbt" 8 "io" 9 "os" 10 ) 11 12 // LevelDat implements the encoding and decoding of level.dat files. An empty 13 // LevelDat is a valid value and may be used to Marshal and Write to a writer or 14 // file afterward. 15 type LevelDat struct { 16 hdr header 17 data []byte 18 } 19 20 // header holds the header for a level.dat file. 21 type header struct { 22 StorageVersion int32 23 FileLength int32 24 } 25 26 // ReadFile reads a level.dat at a path and returns it. 27 func ReadFile(name string) (*LevelDat, error) { 28 f, err := os.Open(name) 29 if err != nil { 30 return nil, fmt.Errorf("level.dat: open file: %w", err) 31 } 32 defer f.Close() 33 return Read(bufio.NewReader(f)) 34 } 35 36 // Read reads a level.dat from r and returns it. 37 func Read(r io.Reader) (*LevelDat, error) { 38 var ldat LevelDat 39 if err := binary.Read(r, binary.LittleEndian, &ldat.hdr); err != nil { 40 return nil, fmt.Errorf("level.dat: read header: %w", err) 41 } 42 ldat.data = make([]byte, ldat.hdr.FileLength) 43 if n, err := r.Read(ldat.data); err != nil || int32(n) != ldat.hdr.FileLength { 44 return nil, fmt.Errorf("level.dat: read data: %w", err) 45 } 46 return &ldat, nil 47 } 48 49 // Unmarshal decodes the level.dat properties from ld into dst. Unmarshal 50 // returns an error if dst was unable to store all properties found in the 51 // level.dat. 52 func (ld *LevelDat) Unmarshal(dst any) error { 53 if err := nbt.UnmarshalEncoding(ld.data, dst, nbt.LittleEndian); err != nil { 54 return fmt.Errorf("level.dat: decode nbt: %w", err) 55 } 56 return nil 57 } 58 59 // Ver returns the version of the level.dat decoded, or 0 if ld is the empty 60 // value. 61 func (ld *LevelDat) Ver() int { 62 return int(ld.hdr.StorageVersion) 63 } 64 65 // Marshal encodes src and stores it in the level.dat. src should be either a 66 // struct or a map of fields. Marshal updates the storage version to the latest. 67 func (ld *LevelDat) Marshal(src any) error { 68 var err error 69 ld.data, err = nbt.MarshalEncoding(src, nbt.LittleEndian) 70 if err != nil { 71 return fmt.Errorf("level.dat: encode nbt: %w", err) 72 } 73 ld.hdr = header{ 74 StorageVersion: Version, 75 FileLength: int32(len(ld.data)), 76 } 77 return nil 78 } 79 80 // Write writes ld to w. 81 func (ld *LevelDat) Write(w io.Writer) error { 82 if err := binary.Write(w, binary.LittleEndian, ld.hdr); err != nil { 83 return fmt.Errorf("level.dat: write header: %w", err) 84 } 85 if _, err := w.Write(ld.data); err != nil { 86 return fmt.Errorf("level.dat: write data: %w", err) 87 } 88 return nil 89 } 90 91 // WriteFile writes ld to a file at name. 92 func (ld *LevelDat) WriteFile(name string) error { 93 f, err := os.OpenFile(name, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) 94 if err != nil { 95 return fmt.Errorf("level.dat: open file: %w", err) 96 } 97 w := bufio.NewWriter(f) 98 defer func() { 99 _ = w.Flush() 100 _ = f.Close() 101 }() 102 return ld.Write(w) 103 }