mirror of
https://github.com/Brawl345/stargazer.git
synced 2024-11-16 21:29:21 +01:00
Add re-packing (WIP)
This commit is contained in:
parent
260c707b73
commit
5a713daee6
30
README.md
30
README.md
@ -1,14 +1,36 @@
|
||||
# Stargazer
|
||||
|
||||
Tool to extract STAR files from the PSX used by its package manager "PackmanJr".
|
||||
Tool to extract and repack STAR files from the PSX used by its package manager "PackmanJr".
|
||||
|
||||
More info: <https://playstationdev.wiki/ps2devwiki/index.php/STAR_Files>
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
stargazer <file> [output dir (optional)]
|
||||
```txt
|
||||
Usage: stargazer <operation> <arguments>
|
||||
|
||||
To extract files:
|
||||
stargazer x <star file> [output dir (optional)]
|
||||
|
||||
To pack a folder:
|
||||
stargazer p <input dir> <star file>
|
||||
```
|
||||
|
||||
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`).
|
||||
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`).
|
||||
|
||||
**NOTE:** Packing is experimental since I have no way to test it and I'm not sure about the limitations of the system (e.g. filenames). I also don't know if the order of the files is relevant.
|
||||
|
||||
## Credits
|
||||
|
||||
Thanks to @martravi for helping with reverse-engineering!
|
||||
|
||||
## Changelog
|
||||
|
||||
### v2.0
|
||||
|
||||
- Add re-packing (experimental)
|
||||
|
||||
### v1.0
|
||||
|
||||
- Initial release
|
||||
|
186
main.go
186
main.go
@ -1,115 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const VERSION = "1.0"
|
||||
const VERSION = "2.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 usage() {
|
||||
fmt.Println("Usage: stargazer <operation> <arguments>")
|
||||
fmt.Println("")
|
||||
fmt.Println(" To extract files:")
|
||||
fmt.Println(" stargazer x <star file> [output dir (optional)]")
|
||||
fmt.Println("")
|
||||
fmt.Println(" To pack a folder:")
|
||||
fmt.Println(" stargazer p <input dir> <star file>")
|
||||
fmt.Println("")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
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)
|
||||
func extract() {
|
||||
inputFile := flag.Arg(1)
|
||||
outputDir := flag.Arg(2)
|
||||
if outputDir == "" {
|
||||
outputDir = fmt.Sprintf("%s_extracted", filepath.Base(strings.TrimSuffix(inputFile, filepath.Ext(inputFile))))
|
||||
}
|
||||
@ -147,3 +65,77 @@ func main() {
|
||||
|
||||
log.Println("Extraction complete!")
|
||||
}
|
||||
|
||||
func pack() {
|
||||
log.Printf("WARNING!!! Packing is experimental and may not work properly!\n")
|
||||
inputDir := flag.Arg(1)
|
||||
outputFile := flag.Arg(2)
|
||||
if outputFile == "" {
|
||||
outputFile = fmt.Sprintf("%s_packed.star", filepath.Base(inputDir))
|
||||
}
|
||||
|
||||
log.Println("Reading files...")
|
||||
var star Star
|
||||
err := filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
entry := Entry{}
|
||||
fp := strings.TrimPrefix(path, inputDir)
|
||||
fp = strings.TrimPrefix(fp, string(os.PathSeparator))
|
||||
entry.FileName = []byte(strings.ReplaceAll(fp, "\\", "/"))
|
||||
entry.Content, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entry.Header.Headersize = uint8(8 + len(entry.GetFileName()))
|
||||
entry.Header.Filesize = uint32(len(entry.Content))
|
||||
entry.Header.FilenameSize = uint8(len(entry.GetFileName()))
|
||||
|
||||
copy(entry.Sha1[:], entry.CalculateSha1())
|
||||
star.Entries = append(star.Entries, entry)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
log.Println("Packing...")
|
||||
file, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
for _, entry := range star.Entries {
|
||||
log.Printf("Packing %s...\n", entry.GetFileName())
|
||||
err := entry.Pack(file)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
log.Println("Packing complete!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Stargazer v%s\n", VERSION)
|
||||
flag.Parse()
|
||||
if flag.NArg() < 1 || flag.NArg() > 3 {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
operation := flag.Arg(0)
|
||||
switch operation {
|
||||
case "x":
|
||||
extract()
|
||||
case "p":
|
||||
pack()
|
||||
default:
|
||||
usage()
|
||||
}
|
||||
}
|
||||
|
118
star.go
Normal file
118
star.go
Normal file
@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type (
|
||||
Header struct {
|
||||
Headersize uint8
|
||||
Padding1 uint8
|
||||
Filesize uint32
|
||||
FilenameSize uint8
|
||||
Padding2 uint8
|
||||
}
|
||||
|
||||
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 (e *Entry) Pack(file *os.File) error {
|
||||
err := binary.Write(file, binary.LittleEndian, e.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = file.Write(e.FileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = file.Write(e.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = file.Write(e.Sha1[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseEntry(file io.Reader) (*Entry, error) {
|
||||
entry := Entry{}
|
||||
err := binary.Read(file, binary.LittleEndian, &entry.Header.Headersize)
|
||||
if err == io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
binary.Read(file, binary.LittleEndian, &entry.Header.Padding1)
|
||||
|
||||
binary.Read(file, binary.LittleEndian, &entry.Header.Filesize)
|
||||
|
||||
binary.Read(file, binary.LittleEndian, &entry.Header.FilenameSize)
|
||||
binary.Read(file, binary.LittleEndian, &entry.Header.Padding2)
|
||||
|
||||
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
|
||||
}
|
Loading…
Reference in New Issue
Block a user