start conversion to Go

This commit is contained in:
Joel Wetzell
2026-04-15 12:43:39 -05:00
parent d2e568bd71
commit f07276b858
8 changed files with 223 additions and 0 deletions
+99
View File
@@ -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)
}
}
+5
View File
@@ -0,0 +1,5 @@
module github.com/jwetzell/jakaudiotools
go 1.26.2
require github.com/urfave/cli/v3 v3.8.0
+10
View File
@@ -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=
+109
View File
@@ -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
}