完成packer
This commit is contained in:
commit
e57ee27b95
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${fileDirname}",
|
||||
"args":[
|
||||
"-z",
|
||||
"-b",
|
||||
"exmaple/example",
|
||||
"-f",
|
||||
"exmaple/res.text",
|
||||
"-f",
|
||||
"exmaple/compress.text",
|
||||
"-o",
|
||||
"exmaple/packedexample"
|
||||
],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Makefile for building the packer command-line tool
|
||||
|
||||
# Variables
|
||||
BINARY_NAME := packer
|
||||
BUILD_DIR := ./bin
|
||||
SRC_DIR := ./cmd/packer
|
||||
|
||||
# Build the packer tool
|
||||
build:
|
||||
@echo "Building $(BINARY_NAME)..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@go build -o $(BUILD_DIR)/$(BINARY_NAME) $(SRC_DIR)
|
||||
@echo "Build completed. Binary saved to $(BUILD_DIR)/$(BINARY_NAME)"
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
@rm -rf $(BUILD_DIR)
|
||||
@echo "Clean completed."
|
||||
|
||||
.PHONY: build clean
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type CountWriter struct {
|
||||
Writer io.Writer
|
||||
Count int64
|
||||
}
|
||||
|
||||
func (cw *CountWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = cw.Writer.Write(p)
|
||||
cw.Count += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
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!")
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Size int64 `json:"size"`
|
||||
ZSize int64 `json:"zsize"`
|
||||
Offset int64 `json:"offset"`
|
||||
}
|
||||
|
||||
func (f *File) Read(pf *os.File, b []byte) (int, error) {
|
||||
if f.ZSize > 0 {
|
||||
compressed := make([]byte, f.ZSize)
|
||||
_, err := pf.ReadAt(compressed, f.Offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Decompress
|
||||
reader := flate.NewReader(bytes.NewReader(compressed))
|
||||
defer reader.Close()
|
||||
n, err := reader.Read(b)
|
||||
if err != nil && err != io.EOF {
|
||||
fmt.Println("Error reading file:", err, n, compressed)
|
||||
return 0, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
return pf.ReadAt(b, f.Offset)
|
||||
}
|
||||
|
||||
type FileSystem struct {
|
||||
Files map[string]File `json:"files"`
|
||||
fp *os.File
|
||||
}
|
||||
|
||||
func (fs *FileSystem) ToJSON() (string, error) {
|
||||
data, err := json.Marshal(fs)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal FileSystem to JSON: %v", err)
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func FromJSON(jsonStr string) (*FileSystem, error) {
|
||||
var fs FileSystem
|
||||
err := json.Unmarshal([]byte(jsonStr), &fs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON to FileSystem: %v", err)
|
||||
}
|
||||
return &fs, nil
|
||||
}
|
||||
|
||||
func (fs *FileSystem) AddFile(name string, size int64, offset int64) {
|
||||
if fs.Files == nil {
|
||||
fs.Files = make(map[string]File)
|
||||
}
|
||||
fs.Files[name] = File{Size: size, Offset: offset}
|
||||
}
|
||||
|
||||
func (fs *FileSystem) GetFile(name string) (File, bool) {
|
||||
file, exists := fs.Files[name]
|
||||
return file, exists
|
||||
}
|
||||
|
||||
func (fs *FileSystem) ListFiles() []string {
|
||||
keys := make([]string, 0, len(fs.Files))
|
||||
for k := range fs.Files {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (fs *FileSystem) ReadFile(name string) ([]byte, int, error) {
|
||||
err := fs.openContainerFile()
|
||||
if err != nil {
|
||||
return []byte{}, 0, fmt.Errorf("failed to open container file: %v", err)
|
||||
}
|
||||
file, exists := fs.GetFile(name)
|
||||
if !exists {
|
||||
return []byte{}, 0, fmt.Errorf("file %s not found", name)
|
||||
}
|
||||
|
||||
b := make([]byte, file.Size)
|
||||
len, err := file.Read(fs.fp, b)
|
||||
return b, len, err
|
||||
}
|
||||
func (fs *FileSystem) openContainerFile() error {
|
||||
if fs.fp != nil {
|
||||
return nil
|
||||
}
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get executable path: %v", err)
|
||||
}
|
||||
fp, err := os.Open(exePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open executable file: %v", err)
|
||||
}
|
||||
fs.fp = fp
|
||||
return nil
|
||||
}
|
||||
func (fs *FileSystem) Close() error {
|
||||
if fs.fp != nil {
|
||||
err := fs.fp.Close()
|
||||
fs.fp = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetFileSystem() (*FileSystem, error) {
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get executable path: %v", err)
|
||||
}
|
||||
fp, err := os.Open(exePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open executable file: %v", err)
|
||||
}
|
||||
defer fp.Close()
|
||||
// Seek to the end to find the JSON metadata
|
||||
stat, err := fp.Stat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to stat executable file: %v", err)
|
||||
}
|
||||
size := stat.Size()
|
||||
if size < 8 {
|
||||
return nil, fmt.Errorf("executable file is too small to contain metadata")
|
||||
}
|
||||
_, err = fp.Seek(-8, io.SeekEnd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to seek to end of executable file: %v", err)
|
||||
}
|
||||
var fsSize uint64
|
||||
err = binary.Read(fp, binary.LittleEndian, &fsSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode metadata size: %v", err)
|
||||
}
|
||||
fmt.Println("fsSize:", fsSize)
|
||||
if fsSize == 0 || fsSize > uint64(size-8) {
|
||||
return nil, fmt.Errorf("invalid metadata size: %d", fsSize)
|
||||
}
|
||||
_, err = fp.Seek(-(8 + int64(fsSize)), io.SeekEnd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to seek to metadata position: %v", err)
|
||||
}
|
||||
jsonData := make([]byte, fsSize)
|
||||
_, err = fp.Read(jsonData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read metadata: %v", err)
|
||||
}
|
||||
var fs FileSystem
|
||||
err = json.Unmarshal(jsonData, &fs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode JSON metadata: %v", err)
|
||||
}
|
||||
return &fs, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}
|
||||
func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}
|
||||
func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}
|
||||
func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}
|
||||
func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}func compress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||
w.Write(data)
|
||||
w.Close()
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func main() {
|
||||
data := []byte("1234567890abcdefghij") // 20 字节随机内容
|
||||
compressed := compress(data)
|
||||
|
||||
fmt.Printf("原始: %d 字节\n", len(data))
|
||||
fmt.Printf("压缩后: %d 字节\n", len(compressed))
|
||||
fmt.Printf("比率: %.2f\n", float64(len(compressed))/float64(len(data)))
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.kingecg.top/kingecg/gopacker/data"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fs, err := data.GetFileSystem()
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return
|
||||
}
|
||||
defer fs.Close()
|
||||
files := fs.ListFiles()
|
||||
fmt.Println("Files:", files)
|
||||
content, length, err := fs.ReadFile("res.text")
|
||||
if err != nil {
|
||||
fmt.Println("Error reading file:", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("File content (%d bytes):\n%s\n", length, string(content))
|
||||
scontent, slen, serr := fs.ReadFile("compress.text")
|
||||
if serr != nil {
|
||||
fmt.Println("Error reading file:", serr)
|
||||
return
|
||||
|
||||
}
|
||||
fmt.Printf("File content (%d bytes):\n%s\n", slen, string(scontent))
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
This is a text file.
|
||||
Loading…
Reference in New Issue