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 }