Initial commit
This commit is contained in:
commit
5fae89a394
|
@ -0,0 +1,61 @@
|
|||
name: Build
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
env:
|
||||
GOVER: ^1.18
|
||||
NAME: stargazer
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
GOOS: [ windows, linux, darwin ]
|
||||
GOARCH: [ amd64, 386, arm, arm64 ]
|
||||
exclude:
|
||||
- GOOS: windows
|
||||
GOARCH: arm
|
||||
- GOOS: darwin
|
||||
GOARCH: 386
|
||||
- GOOS: darwin
|
||||
GOARCH: arm
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GOVER }}
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
GOOS: ${{ matrix.GOOS }}
|
||||
GOARCH: ${{ matrix.GOARCH }}
|
||||
run: go build -ldflags="-s -w" -o dist/$NAME-$GOOS-$GOARCH
|
||||
|
||||
- name: Rename binaries (Windows)
|
||||
if: matrix.GOOS == 'windows'
|
||||
run: for x in dist/$NAME-windows-*; do mv $x $x.exe; done
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{env.NAME}}-${{ matrix.GOOS }}-${{ matrix.GOARCH }}-${{github.sha}}
|
||||
path: dist/*
|
||||
retention-days: 90
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*.iml
|
||||
.idea/
|
|
@ -0,0 +1,24 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <https://unlicense.org>
|
|
@ -0,0 +1,14 @@
|
|||
# Stargazer
|
||||
|
||||
Tool to extract STAR files from the PSX used by its package manager "PackmanJr".
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
./stargazer stargazer <file> [output dir (optional)]
|
||||
```
|
||||
|
||||
If not 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`).
|
||||
|
||||
## Credits
|
||||
Thanks to @martravi for helping with reverse-engineering!
|
|
@ -0,0 +1,149 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const VERSION = "1.0"
|
||||
|
||||
type (
|
||||
Header struct {
|
||||
Unknown1stByte int8
|
||||
Unknown2ndByte int8
|
||||
Filesize uint32
|
||||
FilenameSize int8
|
||||
MaybePadding int8
|
||||
}
|
||||
|
||||
Entry struct {
|
||||
Header
|
||||
FileName []byte
|
||||
Content []byte
|
||||
Sha1 [20]byte
|
||||
}
|
||||
|
||||
Star struct {
|
||||
Entries []Entry
|
||||
}
|
||||
)
|
||||
|
||||
func (e *Entry) GetFileName() string {
|
||||
return string(e.FileName[:])
|
||||
}
|
||||
|
||||
func (e *Entry) CalculateSha1() []byte {
|
||||
h := sha1.New()
|
||||
h.Write(e.Content)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func (e *Entry) GetSha1() string {
|
||||
return fmt.Sprintf("%x", e.Sha1)
|
||||
}
|
||||
|
||||
func (e *Entry) Extract(outputDir string) error {
|
||||
fp := filepath.Join(outputDir, e.GetFileName())
|
||||
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 ParseEntry(file io.Reader) (*Entry, error) {
|
||||
entry := Entry{}
|
||||
err := binary.Read(file, binary.LittleEndian, &entry.Header.Unknown1stByte)
|
||||
if err == io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
binary.Read(file, binary.LittleEndian, &entry.Header.Unknown2ndByte)
|
||||
|
||||
binary.Read(file, binary.LittleEndian, &entry.Header.Filesize)
|
||||
|
||||
binary.Read(file, binary.LittleEndian, &entry.Header.FilenameSize)
|
||||
binary.Read(file, binary.LittleEndian, &entry.Header.MaybePadding)
|
||||
|
||||
filename := make([]byte, entry.Header.FilenameSize)
|
||||
binary.Read(file, binary.LittleEndian, &filename)
|
||||
entry.FileName = filename
|
||||
|
||||
entry.Content = make([]byte, entry.Header.Filesize)
|
||||
binary.Read(file, binary.LittleEndian, &entry.Content)
|
||||
|
||||
binary.Read(file, binary.LittleEndian, &entry.Sha1)
|
||||
|
||||
calculatedHash := entry.CalculateSha1()
|
||||
|
||||
if !bytes.Equal(calculatedHash, entry.Sha1[:]) {
|
||||
log.Fatalln("Hash mismatch")
|
||||
}
|
||||
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Stargazer v%s\n", VERSION)
|
||||
flag.Parse()
|
||||
if flag.NArg() < 1 || flag.NArg() > 2 {
|
||||
fmt.Println("Usage: stargazer <file> [output dir (optional)]")
|
||||
os.Exit(1)
|
||||
}
|
||||
inputFile := flag.Arg(0)
|
||||
outputDir := flag.Arg(1)
|
||||
if outputDir == "" {
|
||||
outputDir = fmt.Sprintf("%s_extracted", filepath.Base(strings.TrimSuffix(inputFile, filepath.Ext(inputFile))))
|
||||
}
|
||||
|
||||
file, err := os.Open(inputFile)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
var star Star
|
||||
|
||||
log.Printf("Parsing '%s'...\n", inputFile)
|
||||
for {
|
||||
entry, err := ParseEntry(file)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
star.Entries = append(star.Entries, *entry)
|
||||
}
|
||||
|
||||
log.Printf("Extracting to '%s'...\n", outputDir)
|
||||
for _, entry := range star.Entries {
|
||||
log.Printf("Extracting %s...\n", entry.GetFileName())
|
||||
err := entry.Extract(outputDir)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Extraction complete!")
|
||||
}
|
Loading…
Reference in New Issue