105 lines
2.7 KiB
Go
105 lines
2.7 KiB
Go
package asar // import "b612.me/asar"
|
||
|
||
import (
|
||
"encoding/binary"
|
||
"errors"
|
||
"fmt"
|
||
"io"
|
||
)
|
||
|
||
var (
|
||
errMalformed = errors.New("asar: malformed archive")
|
||
)
|
||
|
||
// DecodeOptions 用于控制解码行为
|
||
type DecodeOptions struct {
|
||
// StrictValidation 如果为 false,会忽略一些格式验证错误和尾部填充
|
||
StrictValidation bool
|
||
}
|
||
|
||
// DefaultDecodeOptions 默认选项(严格模式)
|
||
var DefaultDecodeOptions = DecodeOptions{
|
||
StrictValidation: true,
|
||
}
|
||
|
||
// Decode decodes the ASAR archive in ra.
|
||
//
|
||
// Returns the root element and nil on success. nil and an error is returned on
|
||
// failure.
|
||
func Decode(ra io.ReaderAt) (*Entry, error) {
|
||
return DecodeWithOptions(ra, DefaultDecodeOptions)
|
||
}
|
||
|
||
// DecodeWithOptions 带选项的解码函数
|
||
func DecodeWithOptions(ra io.ReaderAt, opts DecodeOptions) (*Entry, error) {
|
||
headerSize := uint32(0)
|
||
headerStringSize := uint32(0)
|
||
|
||
// [pickle object header (4 bytes) == 4]
|
||
// [pickle uint32 = $header_object_size]
|
||
{
|
||
var buff [8]byte
|
||
if n, _ := ra.ReadAt(buff[:], 0); n != 8 {
|
||
return nil, errMalformed
|
||
}
|
||
|
||
dataSize := binary.LittleEndian.Uint32(buff[:4])
|
||
if dataSize != 4 {
|
||
return nil, errMalformed
|
||
}
|
||
headerSize = binary.LittleEndian.Uint32(buff[4:8])
|
||
}
|
||
|
||
// [pickle object header (4 bytes)]
|
||
// [pickle data header (4 bytes) == $string_size]
|
||
// [pickle string ($string_size bytes)]
|
||
{
|
||
var buff [8]byte
|
||
if n, _ := ra.ReadAt(buff[:], 8); n != 8 {
|
||
return nil, errMalformed
|
||
}
|
||
|
||
headerObjectSize := binary.LittleEndian.Uint32(buff[:4])
|
||
|
||
// 在宽松模式下放宽验证
|
||
if opts.StrictValidation {
|
||
if headerObjectSize != headerSize-4 {
|
||
return nil, errMalformed
|
||
}
|
||
} else {
|
||
// 宽松模式:允许一定的偏差
|
||
if headerObjectSize > headerSize || headerObjectSize < headerSize-20 {
|
||
return nil, fmt.Errorf("asar: header object size %d is out of reasonable range (header size: %d)",
|
||
headerObjectSize, headerSize)
|
||
}
|
||
}
|
||
|
||
headerStringSize = binary.LittleEndian.Uint32(buff[4:8])
|
||
}
|
||
|
||
// 在宽松模式下,尝试找到实际的 JSON 结束位置
|
||
actualHeaderSize := int64(headerStringSize)
|
||
if !opts.StrictValidation && headerStringSize > 0 {
|
||
// 读取 header 数据来检测实际的 JSON 结束
|
||
tempBuf := make([]byte, headerStringSize)
|
||
if n, _ := ra.ReadAt(tempBuf, 16); n > 0 {
|
||
// 找到最后一个有效的 JSON 结束位置
|
||
if endPos := findJSONEnd(tempBuf); endPos > 0 {
|
||
actualHeaderSize = int64(endPos)
|
||
}
|
||
}
|
||
}
|
||
|
||
// read header string
|
||
headerSection := io.NewSectionReader(ra, 8+8, actualHeaderSize)
|
||
baseOffset := 8 + int64(headerSize)
|
||
baseOffset += baseOffset % 4 // pickle objects are uint32 aligned
|
||
|
||
root, err := decodeHeaderWithOptions(ra, headerSection, baseOffset, opts)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return root, nil
|
||
}
|