mirror of
https://github.com/jwetzell/JakAudioTools.git
synced 2026-07-26 10:08:39 +00:00
start conversion to Go
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jwetzell/jakaudiotools"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := &cli.Command{
|
||||
Name: "vagparser",
|
||||
Usage: "parse VAG files",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "wad",
|
||||
Value: "",
|
||||
Usage: "path to JAK VAGWAD file",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "dict",
|
||||
Value: "",
|
||||
Usage: "path to JAK VAGDIR file to lookup names",
|
||||
Required: true,
|
||||
},
|
||||
&cli.Int8Flag{
|
||||
Name: "game",
|
||||
Value: 1,
|
||||
Usage: "the JAK game the VAG file is from (1,2,3)",
|
||||
Validator: func(flag int8) error {
|
||||
if flag != 1 && flag != 2 && flag != 3 {
|
||||
return fmt.Errorf("game must be either 1, 2, or 3")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "directory to output parsed VAG files to (if not specified, will just print names and counts)",
|
||||
Value: "",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
wadPath := cmd.String("wad")
|
||||
wadData, err := os.ReadFile(wadPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read WAD file: %w", err)
|
||||
}
|
||||
|
||||
dirPath := cmd.String("dict")
|
||||
dirData, err := os.ReadFile(dirPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read dict file: %w", err)
|
||||
}
|
||||
|
||||
game := cmd.Int8("game")
|
||||
|
||||
entries, err := jakaudiotools.ParseVAG(wadData, dirData, game)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse VAG file: %w", err)
|
||||
}
|
||||
|
||||
outputPath := cmd.String("output")
|
||||
if outputPath != "" {
|
||||
if _, err := os.Stat(outputPath); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(outputPath, 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create output directory: %w", err)
|
||||
}
|
||||
}
|
||||
for _, entry := range entries {
|
||||
entryPath := fmt.Sprintf("%s/%s.VAGp", outputPath, entry.Name)
|
||||
fmt.Printf("writing VAG file to %s\n", entryPath)
|
||||
err := os.WriteFile(entryPath, entry.Data, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write VAG file: %w", err)
|
||||
}
|
||||
}
|
||||
fmt.Printf("wrote %d VAG files to %s\n", len(entries), outputPath)
|
||||
} else {
|
||||
for _, entry := range entries {
|
||||
fmt.Printf("VAG file named %s found\n", entry.Name)
|
||||
}
|
||||
fmt.Printf("found %d VAG entries\n", len(entries))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := cmd.Run(context.Background(), os.Args); err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module github.com/jwetzell/jakaudiotools
|
||||
|
||||
go 1.26.2
|
||||
|
||||
require github.com/urfave/cli/v3 v3.8.0
|
||||
@@ -0,0 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/urfave/cli/v3 v3.8.0 h1:XqKPrm0q4P0q5JpoclYoCAv0/MIvH/jZ2umzuf8pNTI=
|
||||
github.com/urfave/cli/v3 v3.8.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,109 @@
|
||||
package jakaudiotools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type VAGEntry struct {
|
||||
Name string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func ParseVAG(vagWAD []byte, vagDict []byte, game int8) ([]VAGEntry, error) {
|
||||
entries := []VAGEntry{}
|
||||
separator := "VAGp"
|
||||
dictionaryEntrySize := 12
|
||||
|
||||
if game != 1 {
|
||||
separator = "pGAV"
|
||||
dictionaryEntrySize = 16
|
||||
}
|
||||
|
||||
magic := []byte(separator)
|
||||
stereo := []byte("Stereo")
|
||||
mono := []byte("Mono")
|
||||
|
||||
stereoInterleaveSize := 8192
|
||||
|
||||
fileStart := 0
|
||||
firstTime := true
|
||||
previousFileStart := 0
|
||||
fileCount := 0
|
||||
skipInterleave := false
|
||||
|
||||
vagDictLookup := processVAGDict(vagDict, dictionaryEntrySize)
|
||||
|
||||
for fileStart != -1 {
|
||||
previousFileStart = fileStart
|
||||
if firstTime {
|
||||
location := bytes.Index(vagWAD[fileStart:], magic)
|
||||
if location != -1 {
|
||||
fileStart += location
|
||||
} else {
|
||||
fileStart = -1
|
||||
}
|
||||
firstTime = false
|
||||
} else {
|
||||
//if last time the start of a stereo file was found we can start looking for the next file starting after the interleave
|
||||
if skipInterleave {
|
||||
location := bytes.Index(vagWAD[fileStart+len(magic)+stereoInterleaveSize:], magic)
|
||||
if location != -1 {
|
||||
fileStart += len(magic) + stereoInterleaveSize + location
|
||||
} else {
|
||||
fileStart = -1
|
||||
}
|
||||
skipInterleave = false
|
||||
} else {
|
||||
location := bytes.Index(vagWAD[fileStart+len(magic):], magic)
|
||||
if location != -1 {
|
||||
fileStart += len(magic) + location
|
||||
} else {
|
||||
fileStart = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
stereoLocation := bytes.Index(vagWAD, stereo)
|
||||
monoLocation := bytes.Index(vagWAD, mono)
|
||||
|
||||
audioType := "mono"
|
||||
if stereoLocation != -1 && (monoLocation == -1 || stereoLocation < monoLocation) {
|
||||
audioType = "stereo"
|
||||
}
|
||||
|
||||
if audioType == "stereo" && !skipInterleave {
|
||||
skipInterleave = true
|
||||
}
|
||||
|
||||
if fileStart >= 0 && fileStart != previousFileStart {
|
||||
fileCount += 1
|
||||
outname := fmt.Sprintf("%d", fileCount)
|
||||
if vagDict != nil && game <= 2 {
|
||||
if fileCount-1 >= len(vagDictLookup) {
|
||||
return nil, fmt.Errorf("file count exceeds number of entries in VAG dict")
|
||||
}
|
||||
outname = vagDictLookup[fileCount-1]
|
||||
}
|
||||
fmt.Printf("%s file named %s found\n", audioType, outname)
|
||||
entry := VAGEntry{
|
||||
Name: outname,
|
||||
Data: vagWAD[previousFileStart:fileStart],
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func processVAGDict(vagDict []byte, entrySize int) []string {
|
||||
names := []string{}
|
||||
for i := 0; i < len(vagDict); i += entrySize {
|
||||
if i+entrySize > len(vagDict) {
|
||||
break
|
||||
}
|
||||
name := string(vagDict[i+4 : i+entrySize])
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
Reference in New Issue
Block a user