Skip to content

Commit cdef0c4

Browse files
committed
fixed optimized strings and versions for older save file versions
Modified `readString` to properly read optimized lengths based on version. `version48` now represents optimized uint16 versions; via the new `ReadFrom` method.
1 parent 2edbc65 commit cdef0c4

File tree

2 files changed

+46
-43
lines changed

2 files changed

+46
-43
lines changed

src/factorio_save.go

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,17 @@ func (h *SaveHeader) ReadFrom(r io.Reader) (err error) {
9090

9191
atLeast016 := !h.FactorioVersion.Less(Version{0, 16, 0, 0})
9292

93-
h.Campaign, err = readString(r, atLeast016)
93+
h.Campaign, err = readString(r, Version(h.FactorioVersion), false)
9494
if err != nil {
9595
return fmt.Errorf("read Campaign: %v", err)
9696
}
9797

98-
h.Name, err = readString(r, atLeast016)
98+
h.Name, err = readString(r, Version(h.FactorioVersion), false)
9999
if err != nil {
100100
return fmt.Errorf("read Name: %v", err)
101101
}
102102

103-
h.BaseMod, err = readString(r, atLeast016)
103+
h.BaseMod, err = readString(r, Version(h.FactorioVersion), false)
104104
if err != nil {
105105
return fmt.Errorf("read BaseMod: %v", err)
106106
}
@@ -123,7 +123,7 @@ func (h *SaveHeader) ReadFrom(r io.Reader) (err error) {
123123
}
124124
h.PlayerWon = scratch[0] != 0
125125

126-
h.NextLevel, err = readString(r, atLeast016)
126+
h.NextLevel, err = readString(r, Version(h.FactorioVersion), false)
127127
if err != nil {
128128
return fmt.Errorf("read NextLevel: %v", err)
129129
}
@@ -156,12 +156,9 @@ func (h *SaveHeader) ReadFrom(r io.Reader) (err error) {
156156
h.AllowNonAdminDebugOptions = scratch[0] != 0
157157
}
158158

159-
var loadedFrom version24
160-
_, err = r.Read(scratch[:3])
159+
var loadedFrom version48
160+
err = loadedFrom.ReadFrom(r, Version(h.FactorioVersion))
161161
if err != nil {
162-
return err
163-
}
164-
if err := loadedFrom.UnmarshalBinary(scratch[:3]); err != nil {
165162
return fmt.Errorf("read LoadedFrom: %v", err)
166163
}
167164
h.LoadedFrom = Version(loadedFrom)
@@ -194,7 +191,7 @@ func (h *SaveHeader) ReadFrom(r io.Reader) (err error) {
194191

195192
var n uint32
196193
if atLeast016 {
197-
n, err = readOptimUint32(r)
194+
n, err = readOptimUint(r, Version(h.FactorioVersion), 32)
198195
if err != nil {
199196
return fmt.Errorf("read num mods: %v", err)
200197
}
@@ -217,27 +214,42 @@ func (h *SaveHeader) ReadFrom(r io.Reader) (err error) {
217214
return nil
218215
}
219216

220-
func readOptimUint32(r io.Reader) (uint32, error) {
217+
func readOptimUint(r io.Reader, v Version, bitSize int) (uint32, error) {
221218
var b [4]byte
222-
_, err := r.Read(b[:1])
223-
if err != nil {
224-
return 0, err
219+
if !v.Less(Version{0, 14, 14, 0}) {
220+
_, err := r.Read(b[:1])
221+
if err != nil {
222+
return 0, err
223+
}
224+
if b[0] != 0xFF {
225+
return uint32(b[0]), nil
226+
}
225227
}
226-
if b[0] != 0xFF {
227-
return uint32(b[0]), nil
228+
229+
if bitSize < 0 || bitSize > 64 || (bitSize%8 != 0) {
230+
panic("invalid bit size")
228231
}
229-
_, err = r.Read(b[:4])
232+
233+
_, err := r.Read(b[:bitSize/8])
230234
if err != nil {
231235
return 0, err
232236
}
233-
return binary.LittleEndian.Uint32(b[:4]), nil
237+
238+
switch bitSize {
239+
case 16:
240+
return uint32(binary.LittleEndian.Uint16(b[:2])), nil
241+
case 32:
242+
return binary.LittleEndian.Uint32(b[:4]), nil
243+
default:
244+
panic("invalid bit size")
245+
}
234246
}
235247

236-
func readString(r io.Reader, optimized bool) (s string, err error) {
248+
func readString(r io.Reader, game Version, forceOptimized bool) (s string, err error) {
237249
var n uint32
238250

239-
if optimized {
240-
n, err = readOptimUint32(r)
251+
if !game.Less(Version{0, 16, 0, 0}) || forceOptimized {
252+
n, err = readOptimUint(r, game, 32)
241253
if err != nil {
242254
return "", err
243255
}
@@ -302,22 +314,19 @@ func (h SaveHeader) readStats(r io.Reader) (stats map[byte][]map[uint16]uint32,
302314
}
303315

304316
func (m *Mod) ReadFrom(r io.Reader, game Version) (err error) {
305-
m.Name, err = readString(r, true)
317+
m.Name, err = readString(r, game, true)
306318
if err != nil {
307319
return fmt.Errorf("read Name: %v", err)
308320
}
309321

310-
var scratch [4]byte
311-
var version version24
312-
_, err = r.Read(scratch[:3])
322+
var version version48
323+
err = version.ReadFrom(r, game)
313324
if err != nil {
314325
return err
315326
}
316-
if err := version.UnmarshalBinary(scratch[:3]); err != nil {
317-
return fmt.Errorf("read Version: %v", err)
318-
}
319327
m.Version = Version(version)
320328

329+
var scratch [4]byte
321330
if game.Greater(Version{0, 15, 0, 91}) {
322331
_, err = r.Read(scratch[:4])
323332
if err != nil {

src/version.go

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/binary"
55
"errors"
66
"fmt"
7+
"io"
78
"strconv"
89
"strings"
910
)
@@ -102,24 +103,17 @@ func (v *version24) UnmarshalBinary(data []byte) error {
102103
return nil
103104
}
104105

105-
// version24 is the 48-bit (16, 16, 16) version structure
106+
// version24 is a 48-bit (16, 16, 16) optimized version structure
106107
type version48 Version
107108

108-
func (v version48) MarshalBinary() (data []byte, err error) {
109-
data = make([]byte, 6)
110-
binary.LittleEndian.PutUint16(data[0:2], uint16(v[0]))
111-
binary.LittleEndian.PutUint16(data[2:4], uint16(v[1]))
112-
binary.LittleEndian.PutUint16(data[4:6], uint16(v[2]))
113-
return data, nil
114-
}
115-
116-
func (v *version48) UnmarshalBinary(data []byte) error {
117-
if len(data) < 6 {
118-
return errors.New("version48.UnmarshalBinary: too few bytes")
109+
func (v *version48) ReadFrom(r io.Reader, game Version) error {
110+
for i := 0; i < 3; i++ {
111+
n, err := readOptimUint(r, game, 16)
112+
if err != nil {
113+
return fmt.Errorf("read part %d of version: %v", i, err)
114+
}
115+
v[i] = uint(n)
119116
}
120-
v[0] = uint(binary.LittleEndian.Uint16(data[0:2]))
121-
v[1] = uint(binary.LittleEndian.Uint16(data[2:4]))
122-
v[2] = uint(binary.LittleEndian.Uint16(data[4:6]))
123117
return nil
124118
}
125119

0 commit comments

Comments
 (0)