Skip to content

Commit

Permalink
Add slim/stats flags, add minor gods and power, add eAPM, add rename
Browse files Browse the repository at this point in the history
Add slim and stats flags to the parse command

Add build command

Add proto power commands and minor god info

Add rename command

Add the ability to add a user defined prefix or suffix to replay files

Add eAPM calculation
  • Loading branch information
jerkeeler committed Jan 13, 2025
1 parent 224958b commit 5f6f395
Show file tree
Hide file tree
Showing 10 changed files with 438 additions and 79 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,24 @@ A CLI for restoring [Age of Mythology: Retold](https://www.ageofempires.com/game
This package was written so that the AoM community could have an easy to use, well-maintained tool for parsing rec files. It is also made so that [aomstats.io](https://aomstats.io) can use it to parse rec files and extract even more stats (e.g., minor god choices and build orders).

Heavily inspired by [loggy's work](https://github.com/erin-fitzpatric/next-aom-gg/blob/main/src/server/recParser/recParser.ts) for aom.gg and his [proof of concept python parser](https://github.com/Logg-y/retoldrecprocessor/blob/main/recprocessor.py). I am unabashedly using his work as a reference to build this package. Some portions may be direct copies.

## Installation

## Usage

### Example Output

## Limitations

## Roadmap

- [x] Minor god support
- [x] Rename files support
- [ ] add eAPM support

- [ ] Add support for team games
- [ ] Add refiners for all command types
- [ ] Add stats calculation
- [ ] Add testing

## Development
18 changes: 16 additions & 2 deletions cmd/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
var outputPath string
var quiet bool = false
var prettyPrint bool = false
var slim bool = false
var stats bool = false

// parseCmd represents the parse command
var parseCmd = &cobra.Command{
Expand All @@ -21,13 +23,18 @@ var parseCmd = &cobra.Command{
Long: `Parses .mythrec files to human-readable json`,
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
if stats && slim {
fmt.Fprintf(os.Stderr, "error: you cannot use stats and slim mode together\n")
return
}

absPath, err := validateAndExpandPath(args[0])
if err != nil {
fmt.Printf("Error with filepath: %v\n", err)
return
}

json, err := parser.ParseToJson(absPath, prettyPrint)
json, err := parser.ParseToJson(absPath, prettyPrint, slim, stats, isGzip)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
return
Expand All @@ -53,7 +60,14 @@ func init() {
rootCmd.AddCommand(parseCmd)
parseCmd.Flags().StringVarP(&outputPath, "output", "o", "", "Save the output JSON to the provided filepath")
parseCmd.Flags().BoolVarP(&quiet, "quiet", "q", false, "Quiet mode, no output to standard output")
parseCmd.Flags().BoolVarP(&prettyPrint, "pretty-print", "p", false, "Pretty print the output JSON")
parseCmd.Flags().BoolVar(&prettyPrint, "pretty-print", false, "Pretty print the output JSON")
parseCmd.Flags().BoolVar(&slim, "slim", false, "Slim mode, don't output game commands")
parseCmd.Flags().BoolVar(
&stats,
"stats",
false,
"Stats mode, add stats to the output, you cannot use this with slim mode",
)

parseCmd.PreRun = func(cmd *cobra.Command, args []string) {
if outputPath == "" {
Expand Down
48 changes: 48 additions & 0 deletions cmd/rename.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cmd

import (
"fmt"
"os"

"github.com/jerkeeler/restoration/parser"
"github.com/spf13/cobra"
)

var (
prefix string
suffix string
)

var renameCmd = &cobra.Command{
Use: "rename [directory]",
Short: "Renames all .mythrec (or .mythrec.gz) in a directory based on player names",
Long: `This command will rename replay files in a directory based on the player names in the .mythrec file.
Only files ending in .mthyrec (or .mythrec.gz if the is-gzip flag is set) will be renamed. All other files will
be ignored. This will override the existing files in the directory.
You can optionally provide a prefix and/or suffix that will be added to the renamed files.
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
inputDir := args[0]

// Validate directory exists
if fileInfo, err := os.Stat(inputDir); err != nil || !fileInfo.IsDir() {
fmt.Fprintf(os.Stderr, "error: '%s' is not a valid directory\n", inputDir)
os.Exit(1)
}

err := parser.RenameRecFiles(inputDir, isGzip, prefix, suffix)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
},
}

func init() {
rootCmd.AddCommand(renameCmd)
renameCmd.Flags().StringVar(&prefix, "prefix", "", "Prefix to add to renamed files")
renameCmd.Flags().StringVar(&suffix, "suffix", "", "Suffix to add to renamed files (before the extension)")
}
3 changes: 3 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/spf13/cobra"
)

var isGzip bool = false

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "restoration",
Expand All @@ -25,6 +27,7 @@ func Execute() {

func init() {
verbose := false
rootCmd.PersistentFlags().BoolVar(&isGzip, "is-gzip", false, "Indicates whether the input files are compressed with gzip")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
opts := &slog.HandlerOptions{
Expand Down
19 changes: 19 additions & 0 deletions parser/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package parser

import (
"bytes"
"compress/gzip"
"compress/zlib"
"encoding/binary"
"fmt"
Expand Down Expand Up @@ -29,6 +30,14 @@ func readBool(data *[]byte, offset int) bool {
return (*data)[offset] != 0
}

func readVector(data *[]byte, offset int) Vector3 {
return Vector3{
X: readInt32(data, offset),
Y: readInt32(data, offset+4),
Z: readInt32(data, offset+8),
}
}

func readString(data *[]byte, offset int) RecString {
/*
Reads the utf-16 little endian encoded at the given offset. Strings are enocde such that the first 2 bytes
Expand Down Expand Up @@ -74,3 +83,13 @@ func Decompressl33t(compressed_array *[]byte) ([]byte, error) {

return io.ReadAll(reader)
}

func DecompressGzip(compressed_array *[]byte) ([]byte, error) {
reader, err := gzip.NewReader(bytes.NewReader(*compressed_array))
if err != nil {
return nil, err
}
defer reader.Close()

return io.ReadAll(reader)
}
Loading

0 comments on commit 5f6f395

Please sign in to comment.