mirror of
https://github.com/Brawl345/stargazer.git
synced 2024-11-16 13:19:20 +01:00
Implement packing (WIP) #1
This commit is contained in:
parent
599ff6d41d
commit
4c2287cc65
22
README.md
22
README.md
@ -17,8 +17,8 @@ USAGE:
|
|||||||
|
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
unpack, u Unpacks files from a STAR file
|
unpack, u Unpacks files from a STAR file
|
||||||
|
pack, p Pack a folder into a STAR file
|
||||||
info, i Shows information about a STAR file
|
info, i Shows information about a STAR file
|
||||||
help, h Shows a list of commands or help for one command
|
|
||||||
|
|
||||||
GLOBAL OPTIONS:
|
GLOBAL OPTIONS:
|
||||||
--help, -h show help (default: false)
|
--help, -h show help (default: false)
|
||||||
@ -26,7 +26,7 @@ GLOBAL OPTIONS:
|
|||||||
--version, -v print the version (default: false)
|
--version, -v print the version (default: false)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Unpack
|
### Unpacking
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
NAME:
|
NAME:
|
||||||
@ -43,6 +43,24 @@ OPTIONS:
|
|||||||
If no output directory is given, the file is extracted to the file name minus the extension plus "`_extracted`" (
|
If no output directory is given, the file is extracted to the file name minus the extension plus "`_extracted`" (
|
||||||
e.g. `xPackmanJr_0.105.star` -> `xPackmanJr_0.105_extracted`). Same goes for packing (it will append `_packed.star`).
|
e.g. `xPackmanJr_0.105.star` -> `xPackmanJr_0.105_extracted`). Same goes for packing (it will append `_packed.star`).
|
||||||
|
|
||||||
|
### Packing
|
||||||
|
|
||||||
|
**NOTE:** The correct order of the files is not implemented yet and there are many unknowns! See [issue #1](https://github.com/Brawl345/stargazer/issues/1).
|
||||||
|
|
||||||
|
```txt
|
||||||
|
NAME:
|
||||||
|
stargazer pack - Pack a folder into a STAR file
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
stargazer pack [command options] [arguments...]
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
--input value, -i value Path to a folder
|
||||||
|
--output value, -o value Output path of the STAR file. Defaults to '<input folder>_packed.star'
|
||||||
|
```
|
||||||
|
|
||||||
|
If no output STAR file is given, the file will be created in the same directory as the stargazer binary with the name of the folder plus `_packed.star`.
|
||||||
|
|
||||||
### Info
|
### Info
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@ -22,6 +23,7 @@ func main() {
|
|||||||
Version: "2.0.0",
|
Version: "2.0.0",
|
||||||
Suggest: true,
|
Suggest: true,
|
||||||
EnableBashCompletion: true,
|
EnableBashCompletion: true,
|
||||||
|
HideHelpCommand: true,
|
||||||
Authors: []*cli.Author{
|
Authors: []*cli.Author{
|
||||||
{
|
{
|
||||||
Name: "Brawl345",
|
Name: "Brawl345",
|
||||||
@ -58,6 +60,28 @@ func main() {
|
|||||||
},
|
},
|
||||||
Action: unpack,
|
Action: unpack,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "pack",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "Pack a folder into a STAR file",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "input",
|
||||||
|
Aliases: []string{"i"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Path to a folder",
|
||||||
|
Destination: &input,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "output",
|
||||||
|
Aliases: []string{"o"},
|
||||||
|
Required: false,
|
||||||
|
Usage: "Output path of the STAR file. Defaults to '<input folder>_packed.star'",
|
||||||
|
Destination: &output,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: pack,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "info",
|
Name: "info",
|
||||||
Aliases: []string{"i"},
|
Aliases: []string{"i"},
|
||||||
@ -110,6 +134,49 @@ func unpack(_ *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pack(_ *cli.Context) error {
|
||||||
|
if output == "" {
|
||||||
|
output = fmt.Sprintf("%s_packed.star", filepath.Base(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !quiet {
|
||||||
|
log.Printf("Will pack to '%s'", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !quiet {
|
||||||
|
log.Printf("Reading '%s'...", input)
|
||||||
|
}
|
||||||
|
|
||||||
|
star, err := stargazer.NewSTARFileFromDirectory(input)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !quiet {
|
||||||
|
log.Printf("Writing to '%s'...\n", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create(output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
writer := bufio.NewWriter(out)
|
||||||
|
_, err = star.WriteTo(writer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func info(_ *cli.Context) error {
|
func info(_ *cli.Context) error {
|
||||||
star, err := stargazer.LoadSTARFromFile(input)
|
star, err := stargazer.LoadSTARFromFile(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
7
pkg/stargazer/const.go
Normal file
7
pkg/stargazer/const.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package stargazer
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxFilenameSize = math.MaxUint8 - 8
|
||||||
|
)
|
@ -3,6 +3,7 @@ package stargazer
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNoEntries = errors.New("invalid STAR file - no file entries found")
|
var ErrNoEntries = errors.New("invalid STAR file - no file entries found")
|
||||||
@ -13,8 +14,40 @@ type (
|
|||||||
Actual string
|
Actual string
|
||||||
Filename string
|
Filename string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrFilenameTooLong struct {
|
||||||
|
Filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrFileTooLarge struct {
|
||||||
|
Filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrNotAFile struct {
|
||||||
|
Filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrNotADirectory struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e ErrSHA1Mismatch) Error() string {
|
func (e ErrSHA1Mismatch) Error() string {
|
||||||
return fmt.Sprintf("SHA1 mismatch on file '%s': expected %s, actual %s", e.Filename, e.Expected, e.Actual)
|
return fmt.Sprintf("SHA1 mismatch on file '%s': expected %s, actual %s", e.Filename, e.Expected, e.Actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e ErrFilenameTooLong) Error() string {
|
||||||
|
return fmt.Sprintf("filename '%s' is too long, needs to be < %d characters.", e.Filename, MaxFilenameSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrFileTooLarge) Error() string {
|
||||||
|
return fmt.Sprintf("file '%s' is too large, needs to be < %d bytes.", e.Filename, math.MaxUint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrNotAFile) Error() string {
|
||||||
|
return fmt.Sprintf("'%s' is not a file.", e.Filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrNotADirectory) Error() string {
|
||||||
|
return fmt.Sprintf("'%s' is not a directory.", e.Path)
|
||||||
|
}
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
package stargazer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
Star struct {
|
|
||||||
Entries []Entry
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry struct {
|
|
||||||
Header
|
|
||||||
Content []byte
|
|
||||||
SHA1 [20]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
Header struct {
|
|
||||||
Headersize uint8
|
|
||||||
Padding1 uint8
|
|
||||||
Filesize uint32
|
|
||||||
FilenameSize uint8
|
|
||||||
Padding2 uint8
|
|
||||||
Filename string
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (e *Entry) SHA1String() string {
|
|
||||||
return fmt.Sprintf("%x", e.SHA1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Entry) Unpack(outputDir string) error {
|
|
||||||
fp := filepath.Join(outputDir, e.Filename)
|
|
||||||
err := os.MkdirAll(filepath.Dir(fp), os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f, err := os.Create(fp)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
_, err = f.Write(e.Content)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Entry) Info() string {
|
|
||||||
var sb strings.Builder
|
|
||||||
|
|
||||||
sb.WriteString(fmt.Sprintf("%s\n", e.Filename))
|
|
||||||
sb.WriteString(fmt.Sprintf(" Filesize: %d bytes\n", e.Header.Filesize))
|
|
||||||
sb.WriteString(fmt.Sprintf(" SHA1: %s\n", e.SHA1String()))
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Star) Unpack(outputDir string) error {
|
|
||||||
for _, e := range s.Entries {
|
|
||||||
err := e.Unpack(outputDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Star) Info() string {
|
|
||||||
var sb strings.Builder
|
|
||||||
|
|
||||||
var contentSize uint64
|
|
||||||
for _, e := range s.Entries {
|
|
||||||
contentSize += uint64(e.Header.Filesize)
|
|
||||||
sb.WriteString(e.Info())
|
|
||||||
sb.WriteString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.WriteString(fmt.Sprintf("Total contents: %d\n", len(s.Entries)))
|
|
||||||
sb.WriteString(fmt.Sprintf("Total content size: %d bytes\n", contentSize))
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
@ -7,7 +7,31 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Star struct {
|
||||||
|
Entries []Entry
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry struct {
|
||||||
|
Header
|
||||||
|
Filename string
|
||||||
|
Content []byte
|
||||||
|
SHA1 [20]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
Header struct {
|
||||||
|
Headersize uint8
|
||||||
|
Padding1 uint8
|
||||||
|
Filesize uint32
|
||||||
|
FilenameSize uint8
|
||||||
|
Padding2 uint8
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseEntry(file io.Reader) (*Entry, error) {
|
func parseEntry(file io.Reader) (*Entry, error) {
|
||||||
@ -60,7 +84,7 @@ func parseEntry(file io.Reader) (*Entry, error) {
|
|||||||
}
|
}
|
||||||
return nil, fmt.Errorf("error reading filename: %v", err)
|
return nil, fmt.Errorf("error reading filename: %v", err)
|
||||||
}
|
}
|
||||||
entry.Header.Filename = string(filename)
|
entry.Filename = string(filename)
|
||||||
|
|
||||||
entry.Content = make([]byte, entry.Header.Filesize)
|
entry.Content = make([]byte, entry.Header.Filesize)
|
||||||
err = binary.Read(file, binary.LittleEndian, &entry.Content)
|
err = binary.Read(file, binary.LittleEndian, &entry.Content)
|
||||||
@ -85,7 +109,7 @@ func parseEntry(file io.Reader) (*Entry, error) {
|
|||||||
|
|
||||||
if !bytes.Equal(calculatedHash, entry.SHA1[:]) {
|
if !bytes.Equal(calculatedHash, entry.SHA1[:]) {
|
||||||
return nil, ErrSHA1Mismatch{
|
return nil, ErrSHA1Mismatch{
|
||||||
Filename: entry.Header.Filename,
|
Filename: entry.Filename,
|
||||||
Expected: hex.EncodeToString(entry.SHA1[:]),
|
Expected: hex.EncodeToString(entry.SHA1[:]),
|
||||||
Actual: hex.EncodeToString(calculatedHash),
|
Actual: hex.EncodeToString(calculatedHash),
|
||||||
}
|
}
|
||||||
@ -94,15 +118,109 @@ func parseEntry(file io.Reader) (*Entry, error) {
|
|||||||
return &entry, nil
|
return &entry, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadSTARFromFile(fp string) (*Star, error) {
|
//NewSTARFileFromDirectory creates a new STAR from a given directory.
|
||||||
|
func NewSTARFileFromDirectory(dir string) (*Star, error) {
|
||||||
|
// TODO: Order files with install.txt and metadata.txt at end
|
||||||
|
|
||||||
|
if !isDir(dir) {
|
||||||
|
return nil, ErrNotADirectory{
|
||||||
|
Path: dir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
star := &Star{}
|
||||||
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
relativePath, err := filepath.Rel(dir, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err := NewEntryFromFile(dir, relativePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
star.Entries = append(star.Entries, *entry)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return star, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewEntryFromFile creates a new STAR entry from a file.
|
||||||
|
//Second parameter is the relative filename of the file in the archive.
|
||||||
|
func NewEntryFromFile(dir string, f string) (*Entry, error) {
|
||||||
|
fp := filepath.Join(dir, f)
|
||||||
file, err := os.Open(fp)
|
file, err := os.Open(fp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
|
entry := &Entry{}
|
||||||
|
|
||||||
|
filename := strings.TrimPrefix(f, string(os.PathSeparator))
|
||||||
|
filename = strings.ReplaceAll(filename, "\\", "/")
|
||||||
|
|
||||||
|
if len(filename) > MaxFilenameSize {
|
||||||
|
return nil, ErrFilenameTooLong{
|
||||||
|
Filename: filename,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Filename = filename
|
||||||
|
entry.Header.FilenameSize = uint8(len(entry.Filename))
|
||||||
|
|
||||||
|
entry.Content, err = io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entry.Content) > math.MaxUint32 {
|
||||||
|
return nil, ErrFileTooLarge{
|
||||||
|
Filename: filename,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Header.Filesize = uint32(len(entry.Content))
|
||||||
|
|
||||||
|
entry.Header.Headersize = uint8(1 + 1 + 4 + 1 + 1 + len(entry.Filename))
|
||||||
|
|
||||||
|
h := sha1.New()
|
||||||
|
h.Write(entry.Content)
|
||||||
|
copy(entry.SHA1[:], h.Sum(nil))
|
||||||
|
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//LoadSTARFromFile loads a STAR file from a filepath.
|
||||||
|
func LoadSTARFromFile(fp string) (*Star, error) {
|
||||||
|
file, err := os.Open(fp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if !isFile(fp) {
|
||||||
|
return nil, ErrNotAFile{
|
||||||
|
Filename: fp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return LoadSTAR(file)
|
return LoadSTAR(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//LoadSTAR loads a STAR file from an io.Reader.
|
||||||
func LoadSTAR(file io.Reader) (*Star, error) {
|
func LoadSTAR(file io.Reader) (*Star, error) {
|
||||||
star := &Star{}
|
star := &Star{}
|
||||||
for {
|
for {
|
||||||
@ -122,3 +240,110 @@ func LoadSTAR(file io.Reader) (*Star, error) {
|
|||||||
|
|
||||||
return star, nil
|
return star, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//SHA1String returns the SHA1 of the file as a hex string.
|
||||||
|
func (e *Entry) SHA1String() string {
|
||||||
|
return fmt.Sprintf("%x", e.SHA1)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unpack unpacks the entry to the given directory.
|
||||||
|
func (e *Entry) Unpack(outputDir string) error {
|
||||||
|
fp := filepath.Join(outputDir, e.Filename)
|
||||||
|
err := os.MkdirAll(filepath.Dir(fp), os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.Create(fp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = f.Write(e.Content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//WriteTo writes the entry to the given io.Writer.
|
||||||
|
func (e *Entry) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
var total int64
|
||||||
|
err := binary.Write(w, binary.LittleEndian, &e.Header)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
total += int64(e.Header.Headersize)
|
||||||
|
|
||||||
|
err = binary.Write(w, binary.LittleEndian, []byte(e.Filename))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
total += int64(e.Header.FilenameSize)
|
||||||
|
|
||||||
|
err = binary.Write(w, binary.LittleEndian, e.Content)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
total += int64(e.Header.Filesize)
|
||||||
|
|
||||||
|
err = binary.Write(w, binary.LittleEndian, e.SHA1)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
total += int64(20)
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//Info returns a string with various information of the entry.
|
||||||
|
func (e *Entry) Info() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
sb.WriteString(fmt.Sprintf("%s\n", e.Filename))
|
||||||
|
sb.WriteString(fmt.Sprintf(" Filesize: %d bytes\n", e.Header.Filesize))
|
||||||
|
sb.WriteString(fmt.Sprintf(" SHA1: %s\n", e.SHA1String()))
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unpack unpacks all entries from the STAR to the given directory.
|
||||||
|
func (s *Star) Unpack(outputDir string) error {
|
||||||
|
for _, e := range s.Entries {
|
||||||
|
err := e.Unpack(outputDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//WriteTo writes the STAR with all entries to the given io.Writer.
|
||||||
|
func (s *Star) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
var total int64
|
||||||
|
for _, e := range s.Entries {
|
||||||
|
n, err := e.WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
return total, err
|
||||||
|
}
|
||||||
|
total += n
|
||||||
|
}
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//Info returns a string with various information of all entries.
|
||||||
|
func (s *Star) Info() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
var contentSize uint64
|
||||||
|
for _, e := range s.Entries {
|
||||||
|
contentSize += uint64(e.Header.Filesize)
|
||||||
|
sb.WriteString(e.Info())
|
||||||
|
sb.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.WriteString(fmt.Sprintf("Total contents: %d\n", len(s.Entries)))
|
||||||
|
sb.WriteString(fmt.Sprintf("Total content size: %d bytes\n", contentSize))
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stargazer
|
package stargazer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
@ -44,8 +45,8 @@ func TestLoadSTARFromFile(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if got.Entries[0].Header.Filename != "NulledFile.rel" {
|
if got.Entries[0].Filename != "NulledFile.rel" {
|
||||||
t.Errorf("Expected filename of first entry to be 'NulledFile.rel', got '%s'", got.Entries[0].Header.Filename)
|
t.Errorf("Expected filename of first entry to be 'NulledFile.rel', got '%s'", got.Entries[0].Filename)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,8 +91,8 @@ func TestLoadSTARFromFile(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if got.Entries[1].Header.Filename != "metadata.xml" {
|
if got.Entries[1].Filename != "metadata.xml" {
|
||||||
t.Errorf("Expected filename of second entry to be 'metadata.xml', got '%s'", got.Entries[1].Header.Filename)
|
t.Errorf("Expected filename of second entry to be 'metadata.xml', got '%s'", got.Entries[1].Filename)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +137,8 @@ func TestLoadSTARFromFile(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if got.Entries[2].Header.Filename != "install.txt" {
|
if got.Entries[2].Filename != "install.txt" {
|
||||||
t.Errorf("Expected filename of third entry to be 'install.txt', got '%s'", got.Entries[2].Header.Filename)
|
t.Errorf("Expected filename of third entry to be 'install.txt', got '%s'", got.Entries[2].Filename)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ func TestLoadSTARFromFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadSTARFromFileFail(t *testing.T) {
|
func TestLoadSTARFromFileNotExisting(t *testing.T) {
|
||||||
_, err := LoadSTARFromFile("invalid.star")
|
_, err := LoadSTARFromFile("invalid.star")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected error, got nil")
|
t.Errorf("Expected error, got nil")
|
||||||
@ -165,6 +166,20 @@ func TestLoadSTARFromFileFail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadSTARFromFileNotAFile(t *testing.T) {
|
||||||
|
_, err := LoadSTARFromFile(".")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error, got nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedErr ErrNotAFile
|
||||||
|
if !errors.As(err, &expectedErr) {
|
||||||
|
t.Errorf("Expected error to be of type ErrNotAFile, got %T", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStar_Unpack(t *testing.T) {
|
func TestStar_Unpack(t *testing.T) {
|
||||||
got, err := LoadSTARFromFile(filepath.Join("..", "..", "testdata", "testfile.star"))
|
got, err := LoadSTARFromFile(filepath.Join("..", "..", "testdata", "testfile.star"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -195,6 +210,136 @@ func TestStar_Unpack(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewEntryFromFile(t *testing.T) {
|
||||||
|
got, err := NewEntryFromFile(filepath.Join("..", "..", "testdata", "unpacked"), filepath.Join("dummyfolder", "NulledFile.rel"))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Filename != "dummyfolder/NulledFile.rel" {
|
||||||
|
t.Errorf("Expected filename to be 'dummyfolder/NulledFile', got '%s'", got.Filename)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Header.Headersize != 34 {
|
||||||
|
t.Errorf("Expected header size to be 34, got %d", got.Header.Headersize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Header.Padding1 != 0 {
|
||||||
|
t.Errorf("Expected first padding to be 0, got %d", got.Header.Padding1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Header.Filesize != 10928 {
|
||||||
|
t.Errorf("Expected filesize to be 10928, got %d", got.Header.Filesize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Header.FilenameSize != 26 {
|
||||||
|
t.Errorf("Expected filename size to be 26, got %d", got.Header.FilenameSize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Header.Padding2 != 0 {
|
||||||
|
t.Errorf("Expected second padding to be 0, got %d", got.Header.Padding2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.SHA1String() != "3d433fcbe9585b05ea877814bad60774ff8a9e7c" {
|
||||||
|
t.Errorf("Expected SHA1 to be '3d433fcbe9585b05ea877814bad60774ff8a9e7c', got '%s'", got.SHA1String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Content == nil {
|
||||||
|
t.Errorf("Expected content to be non-nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint32(len(got.Content)) != got.Header.Filesize {
|
||||||
|
t.Errorf("Content size in header is set to %d bytes, but the file is actually %d bytes long", got.Header.Filesize, len(got.Content))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSTARFileFromDirectory(t *testing.T) {
|
||||||
|
got, err := NewSTARFileFromDirectory(filepath.Join("..", "..", "testdata", "unpacked"))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(got.Entries) != 3 {
|
||||||
|
t.Errorf("Expected 3 entries, got %d", len(got.Entries))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Test entries when the correct order is implemented
|
||||||
|
|
||||||
|
// Check first entry
|
||||||
|
if got.Entries[0].Header.Headersize != 34 {
|
||||||
|
t.Errorf("Expected header size of first entry to be 34 bytes long, got %d", got.Entries[0].Header.Headersize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Entries[0].Header.Padding1 != 0 {
|
||||||
|
t.Errorf("Expected first padding of first entry to be 0, got %d", got.Entries[0].Header.Padding1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Entries[0].Header.Filesize != 10928 {
|
||||||
|
t.Errorf("Expected filesize of first entry to be 10928, got %d", got.Entries[0].Header.Filesize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Entries[0].Header.FilenameSize != 26 {
|
||||||
|
t.Errorf("Expected filename size of first entry to be 26, got %d", got.Entries[0].Header.FilenameSize)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Entries[0].Header.Padding2 != 0 {
|
||||||
|
t.Errorf("Expected second padding of first entry to be 0, got %d", got.Entries[0].Header.Padding2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Entries[0].Filename != "dummyfolder/NulledFile.rel" {
|
||||||
|
t.Errorf("Expected filename of first entry to be 'dummyfolder/NulledFile.rel', got '%s'", got.Entries[0].Filename)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Entries[0].Content == nil {
|
||||||
|
t.Errorf("Expected content of first entry to be non-nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint32(len(got.Entries[0].Content)) != got.Entries[0].Header.Filesize {
|
||||||
|
t.Errorf("Expected content of first entry to be %d bytes long, got %d", got.Entries[0].Header.Filesize, len(got.Entries[0].Content))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if got.Entries[0].SHA1String() != "3d433fcbe9585b05ea877814bad60774ff8a9e7c" {
|
||||||
|
t.Errorf("Expected SHA1 of first entry to be '3d433fcbe9585b05ea877814bad60774ff8a9e7c', got '%s'", got.Entries[0].SHA1String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSTARFileFromDirectoryNotADirectory(t *testing.T) {
|
||||||
|
_, err := NewSTARFileFromDirectory(filepath.Join("..", "..", "testdata", "install.txt"))
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error, got nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedErr ErrNotADirectory
|
||||||
|
if !errors.As(err, &expectedErr) {
|
||||||
|
t.Errorf("Expected error to be of type ErrNotADirectory, got %T", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: More tests for failures (file size, file name size, etc.)
|
||||||
|
|
||||||
func fileExists(fileName string) bool {
|
func fileExists(fileName string) bool {
|
||||||
if _, err := os.Stat(fileName); err == nil {
|
if _, err := os.Stat(fileName); err == nil {
|
||||||
return true
|
return true
|
||||||
|
19
pkg/stargazer/utils.go
Normal file
19
pkg/stargazer/utils.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package stargazer
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func isFile(path string) bool {
|
||||||
|
st, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return st.Mode().IsRegular()
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDir(path string) bool {
|
||||||
|
st, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return st.IsDir()
|
||||||
|
}
|
BIN
testdata/unpacked/dummyfolder/NulledFile.rel
vendored
Normal file
BIN
testdata/unpacked/dummyfolder/NulledFile.rel
vendored
Normal file
Binary file not shown.
9
testdata/unpacked/install.txt
vendored
Normal file
9
testdata/unpacked/install.txt
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# --- Uninstall previous version
|
||||||
|
RemovePackage %%%Name%%% %%%Major%%%
|
||||||
|
DeleteDirectory %%%PackagesDir%%%/%%%Name%%%_%%%Major%%%
|
||||||
|
|
||||||
|
# --- Install new version
|
||||||
|
CreateDirectory %%%PackagesDir%%%/%%%Name%%%_%%%Major%%%
|
||||||
|
CopyDirectory %%%DownloadDir%%%/%%%Name%%%_%%%Major%%%.%%%Minor%%% %%%PackagesDir%%%/%%%Name%%%_%%%Major%%%
|
||||||
|
AddPackage %%%Name%%% %%%Version%%% %%%PackagesDir%%%/%%%Name%%%_%%%Major%%% flash_package
|
||||||
|
|
14
testdata/unpacked/metadata.xml
vendored
Normal file
14
testdata/unpacked/metadata.xml
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Package>
|
||||||
|
<ID>000</ID>
|
||||||
|
<Name>NulledFile</Name>
|
||||||
|
<Title> </Title>
|
||||||
|
<PlatformName>PSXJA3</PlatformName>
|
||||||
|
<Description> </Description>
|
||||||
|
<Files/>
|
||||||
|
<Dependencies/>
|
||||||
|
<Version>
|
||||||
|
<Major>0</Major>
|
||||||
|
<Minor>100</Minor>
|
||||||
|
</Version>
|
||||||
|
</Package>
|
Loading…
Reference in New Issue
Block a user