You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
2.8 KiB
Go

package asar // import "b612.me/asar"
import (
"bytes"
"encoding/binary"
"encoding/json"
"io"
"strconv"
)
type entryEncoder struct {
Contents []io.Reader
CurrentOffset int64
Header bytes.Buffer
Encoder *json.Encoder
}
func (enc *entryEncoder) Write(v interface{}) {
enc.Encoder.Encode(v)
enc.Header.Truncate(enc.Header.Len() - 1) // cut off trailing new line
}
func (enc *entryEncoder) WriteField(key string, v interface{}) {
enc.Write(key)
enc.Header.WriteByte(':')
enc.Write(v)
}
func (enc *entryEncoder) Encode(e *Entry) error {
enc.Header.WriteByte('{')
if e.Flags&FlagDir != 0 {
if e.Flags&FlagUnpacked != 0 {
enc.WriteField("unpacked", true)
enc.Header.WriteByte(',')
}
enc.Write("files")
enc.Header.WriteString(":{")
for i, child := range e.Children {
if i > 0 {
enc.Header.WriteByte(',')
}
if !validFilename(child.Name) {
panic(errHeader)
}
enc.Write(child.Name)
enc.Header.WriteByte(':')
if err := enc.Encode(child); err != nil {
return err
}
}
enc.Header.WriteByte('}')
} else {
enc.Write("size")
enc.Header.WriteByte(':')
enc.Write(e.Size)
if e.Flags&FlagExecutable != 0 {
enc.Header.WriteByte(',')
enc.WriteField("executable", true)
}
enc.Header.WriteByte(',')
if e.Flags&FlagUnpacked == 0 {
enc.WriteField("offset", strconv.FormatInt(enc.CurrentOffset, 10))
enc.CurrentOffset += e.Size
enc.Contents = append(enc.Contents, io.NewSectionReader(e.r, e.Offset+e.baseOffset, e.Size))
} else {
enc.WriteField("unpacked", true)
}
}
enc.Header.WriteByte('}')
return nil
}
// EncodeTo writes an ASAR archive containing Entry's descendants. This function
// is usally called on the root entry.
func (e *Entry) EncodeTo(w io.Writer) (n int64, err error) {
defer func() {
if r := recover(); r != nil {
if e := r.(error); e != nil {
err = e
} else {
panic(r)
}
}
}()
encoder := entryEncoder{}
{
var reserve [16]byte
encoder.Header.Write(reserve[:])
}
encoder.Encoder = json.NewEncoder(&encoder.Header)
if err = encoder.Encode(e); err != nil {
return
}
length := encoder.Header.Len() - 16
var newLen int
{
var padding [3]byte
if mod := length % 4; mod != 0 {
encoder.Header.Write(padding[:4-mod])
}
newLen = encoder.Header.Len() - 16
}
header := encoder.Header.Bytes()
binary.LittleEndian.PutUint32(header[:4], 4)
binary.LittleEndian.PutUint32(header[4:8], 8+uint32(newLen))
binary.LittleEndian.PutUint32(header[8:12], 4+uint32(newLen))
binary.LittleEndian.PutUint32(header[12:16], uint32(length))
n, err = encoder.Header.WriteTo(w)
if err != nil {
return
}
for _, chunk := range encoder.Contents {
var written int64
written, err = io.Copy(w, chunk)
n += written
if err != nil {
return
}
}
return
}