package main import ( "encoding/binary" "compress/flate" "flag" "fmt" "git.kingecg.top/kingecg/gopacker/data" "io" "os" "path/filepath" "strings" ) type stringSlice []string // 实现 flag.Value 接口 func (s *stringSlice) String() string { return strings.Join(*s, ",") } func (s *stringSlice) Set(value string) error { *s = append(*s, value) return nil } func addFile(bootPathDir string, filePath string, outputFile *os.File, fs *data.FileSystem) error { relPath, err := filepath.Rel(bootPathDir, filePath) if err != nil { return err } relPath = strings.ReplaceAll(relPath, "\\\\", "/") file, err := os.Open(filePath) if err != nil { return err } defer file.Close() fileInfo, err := file.Stat() if err != nil { return err } // Get current offset in output file offset, err := outputFile.Seek(0, io.SeekCurrent) if err != nil { return err } // Copy file content to output zippedSize := int64(0) if needZip { c := &CountWriter{Writer: outputFile} zipWriter, err := flate.NewWriter(c, flate.BestCompression) if err != nil { return err } _, err = io.Copy(zipWriter, file) // if err != nil { return err } err = zipWriter.Close() if err != nil { return err } zippedSize = c.Count // fmt.Println("zippedSize:", zippedSize) } else { _, err = io.Copy(outputFile, file) if err != nil { return err } } // Record file info in FileSystem fs.Files[relPath] = data.File{ Size: fileInfo.Size(), ZSize: zippedSize, Offset: offset, } return nil } var needZip bool func main() { var filePaths stringSlice bootPath := flag.String("b", "", "Path to the boot program") flag.BoolVar(&needZip, "z", false, "Whether to zip the output file") flag.Var(&filePaths, "f", "Paths to resource files or directories") outputPath := flag.String("o", "", "Path to the output file") flag.Parse() if *bootPath == "" || *outputPath == "" { fmt.Println("Error: boot path (-b) and output path (-o) are required") os.Exit(1) } // Open boot file bootFile, err := os.Open(*bootPath) if err != nil { fmt.Printf("Error opening boot file: %v\n", err) os.Exit(1) } defer bootFile.Close() bootPathDir := filepath.Dir(*bootPath) // Create output file outputFile, err := os.Create(*outputPath) if err != nil { fmt.Printf("Error creating output file: %v\n", err) os.Exit(1) } defer outputFile.Close() // Copy boot file to output _, err = io.Copy(outputFile, bootFile) if err != nil { fmt.Printf("Error copying boot file: %v\n", err) os.Exit(1) } // Initialize FileSystem fs := data.FileSystem{ Files: make(map[string]data.File), } // Process resource files fps := []string(filePaths) for _, filePath := range fps { fileInfo, err := os.Stat(filePath) if err != nil { fmt.Printf("Error accessing resource file: %v\n", err) os.Exit(1) } if fileInfo.IsDir() { // Handle directory err := filepath.Walk(filePath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { err := addFile(bootPathDir, path, outputFile, &fs) if err != nil { fmt.Printf("Error processing file: %v\n", err) return err } } return nil }) if err != nil { fmt.Printf("Error processing directory: %v\n", err) os.Exit(1) } } else { // Handle single file err := addFile(bootPathDir, filePath, outputFile, &fs) if err != nil { fmt.Printf("Error processing file: %v\n", err) os.Exit(1) } } } // Serialize FileSystem to JSON jsonData, err := fs.ToJSON() if err != nil { fmt.Printf("Error serializing FileSystem: %v\n", err) os.Exit(1) } // Write JSON data to output file jsonBytes := []byte(jsonData) _, err = outputFile.Write(jsonBytes) if err != nil { fmt.Printf("Error writing JSON data: %v\n", err) os.Exit(1) } // Write JSON data length (8 bytes) jsonLen := uint64(len(jsonBytes)) err = binary.Write(outputFile, binary.LittleEndian, jsonLen) if err != nil { fmt.Printf("Error writing JSON length: %v\n", err) os.Exit(1) } fmt.Println("Packaging completed successfully!") // change file mod to be executable err = os.Chmod(*outputPath, 0755) if err != nil { fmt.Printf("Error setting executable permission: %v\n", err) os.Exit(1) } fmt.Println("Set executable permission successfully!") }