gopacker/data/data.go

168 lines
3.9 KiB
Go

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
}