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  }