263 lines
4.9 KiB
Go
263 lines
4.9 KiB
Go
package asar // import "b612.me/asar"
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
var (
|
|
errHeader = errors.New("asar: invalid file header")
|
|
)
|
|
|
|
type jsonReader struct {
|
|
ASAR io.ReaderAt
|
|
BaseOffset int64
|
|
D *json.Decoder
|
|
Token json.Token
|
|
Options DecodeOptions // 新增字段
|
|
}
|
|
|
|
func (j *jsonReader) Peek() json.Token {
|
|
if j.Token != nil {
|
|
return j.Token
|
|
}
|
|
tkn, err := j.D.Token()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return nil
|
|
}
|
|
panic(err)
|
|
}
|
|
j.Token = tkn
|
|
return tkn
|
|
}
|
|
|
|
func (j *jsonReader) HasDelimRune(r rune) bool {
|
|
peek := j.Peek()
|
|
ru, ok := peek.(json.Delim)
|
|
if !ok {
|
|
return false
|
|
}
|
|
if rune(ru) != r {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (j *jsonReader) Next() json.Token {
|
|
if j.Token != nil {
|
|
t := j.Token
|
|
j.Token = nil
|
|
return t
|
|
}
|
|
|
|
tkn, err := j.D.Token()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return nil
|
|
}
|
|
panic(err)
|
|
}
|
|
return tkn
|
|
}
|
|
|
|
func (j *jsonReader) NextDelimRune() rune {
|
|
tkn := j.Next()
|
|
r, ok := tkn.(json.Delim)
|
|
if !ok {
|
|
panic(errHeader)
|
|
}
|
|
return rune(r)
|
|
}
|
|
|
|
func (j *jsonReader) ExpectDelim(r rune) {
|
|
next := j.NextDelimRune()
|
|
if next != r {
|
|
panic(errHeader)
|
|
}
|
|
}
|
|
|
|
func (j *jsonReader) ExpectBool() bool {
|
|
tkn := j.Next()
|
|
b, ok := tkn.(bool)
|
|
if !ok {
|
|
panic(errHeader)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (j *jsonReader) ExpectString() string {
|
|
next := j.Next()
|
|
str, ok := next.(string)
|
|
if !ok {
|
|
panic(errHeader)
|
|
}
|
|
return str
|
|
}
|
|
|
|
func (j *jsonReader) ExpectStringVal(val string) {
|
|
str := j.ExpectString()
|
|
if str != val {
|
|
panic(errHeader)
|
|
}
|
|
}
|
|
|
|
func (j *jsonReader) ExpectInt64() int64 {
|
|
var number json.Number
|
|
switch j.Peek().(type) {
|
|
case string:
|
|
number = json.Number(j.ExpectString())
|
|
case json.Number:
|
|
number = j.Next().(json.Number)
|
|
default:
|
|
panic(errHeader)
|
|
}
|
|
val, err := number.Int64()
|
|
if err != nil {
|
|
panic(errHeader)
|
|
}
|
|
return val
|
|
}
|
|
|
|
func parseRoot(r *jsonReader) *Entry {
|
|
entry := &Entry{
|
|
Flags: FlagDir,
|
|
}
|
|
|
|
r.ExpectDelim('{')
|
|
r.ExpectStringVal("files")
|
|
parseFiles(r, entry)
|
|
r.ExpectDelim('}')
|
|
|
|
// 关键修改:根据选项决定是否检查剩余内容
|
|
if r.Options.StrictValidation {
|
|
// 严格模式:不允许有剩余数据
|
|
if r.Next() != nil {
|
|
panic(errHeader)
|
|
}
|
|
} else {
|
|
// 宽松模式:消费并忽略剩余的 token
|
|
// 这些可能是 NUL 填充或其他无关数据
|
|
for {
|
|
token := r.Next()
|
|
if token == nil {
|
|
break
|
|
}
|
|
// 可选:如果需要,可以在这里记录日志
|
|
// log.Printf("Ignoring trailing token in ASAR header: %v", token)
|
|
}
|
|
}
|
|
|
|
return entry
|
|
}
|
|
|
|
func parseFiles(r *jsonReader, parent *Entry) {
|
|
r.ExpectDelim('{')
|
|
for !r.HasDelimRune('}') {
|
|
parseEntry(r, parent)
|
|
}
|
|
r.ExpectDelim('}')
|
|
}
|
|
|
|
func parseEntry(r *jsonReader, parent *Entry) {
|
|
name := r.ExpectString()
|
|
if name == "" {
|
|
panic(errHeader)
|
|
}
|
|
if !validFilename(name) {
|
|
panic(errHeader)
|
|
}
|
|
|
|
r.ExpectDelim('{')
|
|
|
|
child := &Entry{
|
|
Name: name,
|
|
Parent: parent,
|
|
}
|
|
|
|
for !r.HasDelimRune('}') {
|
|
key := r.ExpectString()
|
|
switch key {
|
|
case "files":
|
|
child.Flags |= FlagDir
|
|
parseFiles(r, child)
|
|
case "size":
|
|
child.Size = r.ExpectInt64()
|
|
case "offset":
|
|
child.Offset = r.ExpectInt64()
|
|
case "unpacked":
|
|
if r.ExpectBool() {
|
|
child.Flags |= FlagUnpacked
|
|
}
|
|
case "executable":
|
|
if r.ExpectBool() {
|
|
child.Flags |= FlagExecutable
|
|
}
|
|
default:
|
|
if r.Options.StrictValidation {
|
|
// 严格模式:未知字段导致错误
|
|
panic(errHeader)
|
|
} else {
|
|
// 宽松模式:跳过未知字段
|
|
// 需要消费这个未知字段的值
|
|
if token := r.Next(); token == nil {
|
|
panic(errHeader)
|
|
}
|
|
// 可选:记录日志
|
|
// log.Printf("Ignoring unknown field '%s' in ASAR entry", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
if child.Flags&FlagDir == 0 {
|
|
child.r = r.ASAR
|
|
child.baseOffset = r.BaseOffset
|
|
}
|
|
|
|
parent.Children = append(parent.Children, child)
|
|
r.ExpectDelim('}')
|
|
}
|
|
|
|
// 保留原有的 decodeHeader
|
|
func decodeHeader(asar io.ReaderAt, header *io.SectionReader, offset int64) (entry *Entry, err error) {
|
|
return decodeHeaderWithOptions(asar, header, offset, DefaultDecodeOptions)
|
|
}
|
|
|
|
// 新增带选项的版本
|
|
func decodeHeaderWithOptions(asar io.ReaderAt, header *io.SectionReader, offset int64, opts DecodeOptions) (entry *Entry, err error) {
|
|
decoder := json.NewDecoder(header)
|
|
decoder.UseNumber()
|
|
reader := jsonReader{
|
|
ASAR: asar,
|
|
BaseOffset: offset,
|
|
D: decoder,
|
|
Options: opts, // 传递选项
|
|
}
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
if e := r.(error); e != nil {
|
|
err = e
|
|
} else {
|
|
panic(r)
|
|
}
|
|
}
|
|
}()
|
|
entry = parseRoot(&reader)
|
|
return
|
|
}
|
|
|
|
// findJSONEnd 找到 JSON 的实际结束位置(最后一个有意义的字符)
|
|
func findJSONEnd(data []byte) int {
|
|
// 从后往前找,跳过所有的 NUL 和空白字符
|
|
for i := len(data) - 1; i >= 0; i-- {
|
|
b := data[i]
|
|
// 跳过 NUL 和空白字符
|
|
if b != 0 && b != ' ' && b != '\n' && b != '\r' && b != '\t' {
|
|
// 找到了非空白字符,这应该是 JSON 的结束
|
|
return i + 1
|
|
}
|
|
}
|
|
return len(data)
|
|
}
|