mirror of
https://github.com/Luzifer/scs-extract.git
synced 2024-11-09 12:30:00 +00:00
132 lines
3.1 KiB
Go
132 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
|
|
"github.com/Luzifer/go_helpers/v2/str"
|
|
"github.com/Luzifer/rconfig/v2"
|
|
"github.com/Luzifer/scs-extract/scs"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var (
|
|
cfg = struct {
|
|
Dest string `flag:"dest,d" default:"." description:"Path prefix to use to extract files to"`
|
|
Extract bool `flag:"extract,x" default:"false" description:"Extract files (if not given files are just listed)"`
|
|
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
|
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
|
}{}
|
|
|
|
version = "dev"
|
|
)
|
|
|
|
func init() {
|
|
if err := rconfig.ParseAndValidate(&cfg); err != nil {
|
|
log.Fatalf("Unable to parse commandline options: %s", err)
|
|
}
|
|
|
|
if cfg.VersionAndExit {
|
|
fmt.Printf("scs-extract %s\n", version)
|
|
os.Exit(0)
|
|
}
|
|
|
|
if l, err := log.ParseLevel(cfg.LogLevel); err != nil {
|
|
log.WithError(err).Fatal("Unable to parse log level")
|
|
} else {
|
|
log.SetLevel(l)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
var (
|
|
archive string
|
|
extract []string
|
|
)
|
|
|
|
switch len(rconfig.Args()) {
|
|
case 1:
|
|
// No positional arguments
|
|
log.Fatal("No SCS archive given")
|
|
|
|
case 2:
|
|
archive = rconfig.Args()[1]
|
|
|
|
default:
|
|
archive = rconfig.Args()[1]
|
|
extract = rconfig.Args()[2:]
|
|
}
|
|
|
|
f, err := os.Open(archive)
|
|
if err != nil {
|
|
log.WithError(err).Fatal("Unable to open input file")
|
|
}
|
|
defer f.Close()
|
|
|
|
r, err := scs.NewReader(f, 0)
|
|
if err != nil {
|
|
log.WithError(err).Fatal("Unable to read SCS file headers")
|
|
}
|
|
|
|
log.WithField("no_files", len(r.Files)).Debug("Opened archive")
|
|
|
|
destInfo, err := os.Stat(cfg.Dest)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
log.WithError(err).Fatal("Unable to access destination")
|
|
}
|
|
|
|
if err := os.MkdirAll(cfg.Dest, 0755); err != nil {
|
|
log.WithError(err).Fatal("Unable to create destination directory")
|
|
}
|
|
}
|
|
|
|
if destInfo != nil && !destInfo.IsDir() {
|
|
log.Fatal("Destination exists and is no directory")
|
|
}
|
|
|
|
for _, file := range r.Files {
|
|
if !str.StringInSlice(file.Name, extract) && len(extract) > 0 {
|
|
// Files to extract are given but this is not mentioned
|
|
continue
|
|
}
|
|
|
|
if file.Type == scs.EntryTypeCompressedNames || file.Type == scs.EntryTypeCompressedNamesCopy ||
|
|
file.Type == scs.EntryTypeUncompressedNames || file.Type == scs.EntryTypeUncompressedNamesCopy {
|
|
// Don't care about directories, if they contain files they will be created
|
|
continue
|
|
}
|
|
|
|
if !cfg.Extract {
|
|
// Not asked to extract, do not extract
|
|
fmt.Println(file.Name)
|
|
continue
|
|
}
|
|
|
|
destPath := path.Join(cfg.Dest, file.Name)
|
|
if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
|
|
log.WithError(err).Fatal("Unable to create directory")
|
|
}
|
|
|
|
src, err := file.Open()
|
|
if err != nil {
|
|
log.WithError(err).Fatal("Unable to open file from archive")
|
|
}
|
|
|
|
dest, err := os.Create(destPath)
|
|
if err != nil {
|
|
log.WithError(err).Fatal("Unable to create destination file")
|
|
}
|
|
|
|
if _, err = io.Copy(dest, src); err != nil {
|
|
log.WithError(err).Fatal("Unable to write file contents")
|
|
}
|
|
|
|
dest.Close()
|
|
src.Close()
|
|
|
|
log.WithField("file", file.Name).Info("File extracted")
|
|
}
|
|
}
|