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