114 lines
2.3 KiB
Go
114 lines
2.3 KiB
Go
|
package housekeeper
|
||
|
|
||
|
import (
|
||
|
"encoding"
|
||
|
"errors"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
const cachedBeatmapBinSize = 8 + 1 + 15 + 15 + 8
|
||
|
|
||
|
func b2i(b bool) byte {
|
||
|
if b {
|
||
|
return 1
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
func marshalBinaryCopy(dst []byte, t encoding.BinaryMarshaler) {
|
||
|
b, _ := t.MarshalBinary()
|
||
|
copy(dst, b)
|
||
|
}
|
||
|
|
||
|
// copied from binary.BigEndian
|
||
|
func putUint64(b []byte, v uint64) {
|
||
|
_ = b[7] // boundary check
|
||
|
b[0] = byte(v >> 56)
|
||
|
b[1] = byte(v >> 48)
|
||
|
b[2] = byte(v >> 40)
|
||
|
b[3] = byte(v >> 32)
|
||
|
b[4] = byte(v >> 24)
|
||
|
b[5] = byte(v >> 16)
|
||
|
b[6] = byte(v >> 8)
|
||
|
b[7] = byte(v)
|
||
|
}
|
||
|
|
||
|
func writeBeatmaps(w io.Writer, c []*CachedBeatmap) error {
|
||
|
_, err := w.Write(append([]byte("CGBIN001"), cachedBeatmapBinSize))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
for _, b := range c {
|
||
|
if b == nil || !b.isDownloaded {
|
||
|
continue
|
||
|
}
|
||
|
enc := make([]byte, cachedBeatmapBinSize)
|
||
|
putUint64(enc[:8], uint64(b.ID))
|
||
|
enc[8] = b2i(b.NoVideo)
|
||
|
marshalBinaryCopy(enc[9:24], b.LastUpdate)
|
||
|
marshalBinaryCopy(enc[24:39], b.lastRequested)
|
||
|
putUint64(enc[39:47], b.fileSize)
|
||
|
_, err := w.Write(enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// copied from binary.BigEndian
|
||
|
func readUint64(b []byte) uint64 {
|
||
|
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||
|
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||
|
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||
|
|
||
|
}
|
||
|
|
||
|
func readCachedBeatmap(b []byte) *CachedBeatmap {
|
||
|
m := &CachedBeatmap{}
|
||
|
m.ID = int(readUint64(b[:8]))
|
||
|
m.NoVideo = b[8] == 1
|
||
|
(&m.LastUpdate).UnmarshalBinary(b[9:24])
|
||
|
(&m.lastRequested).UnmarshalBinary(b[24:39])
|
||
|
m.fileSize = readUint64(b[39:47])
|
||
|
m.isDownloaded = true
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
func readBeatmaps(r io.Reader) ([]*CachedBeatmap, error) {
|
||
|
b := make([]byte, 8)
|
||
|
_, err := r.Read(b)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if string(b) != "CGBIN001" {
|
||
|
return nil, errors.New("unknown cgbin version")
|
||
|
}
|
||
|
|
||
|
b = make([]byte, 1)
|
||
|
_, err = r.Read(b)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
bmLength := b[0]
|
||
|
if bmLength == 0 {
|
||
|
return nil, nil
|
||
|
}
|
||
|
b = make([]byte, bmLength)
|
||
|
beatmaps := make([]*CachedBeatmap, 0, 50)
|
||
|
|
||
|
for {
|
||
|
read, err := r.Read(b)
|
||
|
switch {
|
||
|
case err == io.EOF:
|
||
|
return beatmaps, nil
|
||
|
case err != nil:
|
||
|
return nil, err
|
||
|
case byte(read) != bmLength:
|
||
|
return beatmaps, nil
|
||
|
}
|
||
|
beatmaps = append(beatmaps, readCachedBeatmap(b))
|
||
|
}
|
||
|
}
|