mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-08-07 14:25:17 +03:00
166 lines
4.3 KiB
Go
166 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"crypto/sha256"
|
|
"flag"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/sagernet/sing-box/log"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
)
|
|
|
|
var outputPath string
|
|
|
|
func init() {
|
|
flag.StringVar(&outputPath, "output", "", "output AAR path")
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
err := merge()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func merge() error {
|
|
inputPaths := flag.Args()
|
|
if outputPath == "" {
|
|
return E.New("missing output path")
|
|
}
|
|
if len(inputPaths) == 0 {
|
|
return E.New("missing input AAR paths")
|
|
}
|
|
archiveReaders := make([]*zip.ReadCloser, 0, len(inputPaths))
|
|
for _, inputPath := range inputPaths {
|
|
archiveReader, err := zip.OpenReader(inputPath)
|
|
if err != nil {
|
|
return E.Cause(err, "open input AAR: ", inputPath)
|
|
}
|
|
archiveReaders = append(archiveReaders, archiveReader)
|
|
}
|
|
defer func() {
|
|
for _, archiveReader := range archiveReaders {
|
|
archiveReader.Close()
|
|
}
|
|
}()
|
|
|
|
referenceEntries := make(map[string][sha256.Size]byte)
|
|
selectedEntries := make([]*zip.File, 0)
|
|
selectedJNIEntries := make(map[string]bool)
|
|
for inputIndex, archiveReader := range archiveReaders {
|
|
seenEntries := make(map[string]bool)
|
|
for _, archiveFile := range archiveReader.File {
|
|
if strings.HasPrefix(archiveFile.Name, "jni/") {
|
|
if archiveFile.FileInfo().IsDir() {
|
|
continue
|
|
}
|
|
if selectedJNIEntries[archiveFile.Name] {
|
|
return E.New("duplicate AAR JNI entry: ", archiveFile.Name)
|
|
}
|
|
selectedJNIEntries[archiveFile.Name] = true
|
|
selectedEntries = append(selectedEntries, archiveFile)
|
|
continue
|
|
}
|
|
entryDigest, err := digestEntry(archiveFile)
|
|
if err != nil {
|
|
return E.Cause(err, "read AAR entry: ", archiveFile.Name)
|
|
}
|
|
if inputIndex == 0 {
|
|
referenceEntries[archiveFile.Name] = entryDigest
|
|
selectedEntries = append(selectedEntries, archiveFile)
|
|
} else {
|
|
referenceDigest, loaded := referenceEntries[archiveFile.Name]
|
|
if !loaded {
|
|
return E.New("unexpected AAR entry: ", archiveFile.Name)
|
|
}
|
|
if referenceDigest != entryDigest {
|
|
return E.New("AAR entry differs between architectures: ", archiveFile.Name)
|
|
}
|
|
}
|
|
seenEntries[archiveFile.Name] = true
|
|
}
|
|
if inputIndex > 0 {
|
|
for referenceName := range referenceEntries {
|
|
if !seenEntries[referenceName] {
|
|
return E.New("missing AAR entry: ", referenceName)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
absoluteOutputPath, err := filepath.Abs(outputPath)
|
|
if err != nil {
|
|
return E.Cause(err, "resolve output AAR path")
|
|
}
|
|
err = os.MkdirAll(filepath.Dir(absoluteOutputPath), 0o755)
|
|
if err != nil {
|
|
return E.Cause(err, "create output AAR directory")
|
|
}
|
|
temporaryFile, err := os.CreateTemp(filepath.Dir(absoluteOutputPath), ".merge-aar-*.aar")
|
|
if err != nil {
|
|
return E.Cause(err, "create temporary output AAR")
|
|
}
|
|
temporaryPath := temporaryFile.Name()
|
|
defer os.Remove(temporaryPath)
|
|
archiveWriter := zip.NewWriter(temporaryFile)
|
|
for _, archiveFile := range selectedEntries {
|
|
rawReader, openErr := archiveFile.OpenRaw()
|
|
if openErr != nil {
|
|
archiveWriter.Close()
|
|
temporaryFile.Close()
|
|
return E.Cause(openErr, "open raw AAR entry: ", archiveFile.Name)
|
|
}
|
|
header := archiveFile.FileHeader
|
|
rawWriter, createErr := archiveWriter.CreateRaw(&header)
|
|
if createErr != nil {
|
|
archiveWriter.Close()
|
|
temporaryFile.Close()
|
|
return E.Cause(createErr, "create output AAR entry: ", archiveFile.Name)
|
|
}
|
|
_, copyErr := io.Copy(rawWriter, rawReader)
|
|
if copyErr != nil {
|
|
archiveWriter.Close()
|
|
temporaryFile.Close()
|
|
return E.Cause(copyErr, "copy output AAR entry: ", archiveFile.Name)
|
|
}
|
|
}
|
|
err = archiveWriter.Close()
|
|
if err != nil {
|
|
temporaryFile.Close()
|
|
return E.Cause(err, "finalize output AAR")
|
|
}
|
|
err = temporaryFile.Close()
|
|
if err != nil {
|
|
return E.Cause(err, "close output AAR")
|
|
}
|
|
err = os.Rename(temporaryPath, absoluteOutputPath)
|
|
if err != nil {
|
|
return E.Cause(err, "replace output AAR")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func digestEntry(archiveFile *zip.File) ([sha256.Size]byte, error) {
|
|
entryReader, err := archiveFile.Open()
|
|
if err != nil {
|
|
return [sha256.Size]byte{}, err
|
|
}
|
|
digest := sha256.New()
|
|
_, err = io.Copy(digest, entryReader)
|
|
closeErr := entryReader.Close()
|
|
if err != nil {
|
|
return [sha256.Size]byte{}, err
|
|
}
|
|
if closeErr != nil {
|
|
return [sha256.Size]byte{}, closeErr
|
|
}
|
|
var result [sha256.Size]byte
|
|
copy(result[:], digest.Sum(nil))
|
|
return result, nil
|
|
}
|