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
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
|
|
}
|