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