umadump/replay: implement race sim decoding
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
// Code generated by "stringer -type RunningStyle -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package replay
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[StyleNone-0]
|
||||||
|
_ = x[StyleNige-1]
|
||||||
|
_ = x[StyleSenko-2]
|
||||||
|
_ = x[StyleSashi-3]
|
||||||
|
_ = x[StyleOikomi-4]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _RunningStyle_name = "NONENIGESENKOSASHIOIKOMI"
|
||||||
|
|
||||||
|
var _RunningStyle_index = [...]uint8{0, 4, 8, 13, 18, 24}
|
||||||
|
|
||||||
|
func (i RunningStyle) String() string {
|
||||||
|
idx := int(i) - 0
|
||||||
|
if i < 0 || idx >= len(_RunningStyle_index)-1 {
|
||||||
|
return "RunningStyle(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _RunningStyle_name[_RunningStyle_index[idx]:_RunningStyle_index[idx+1]]
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,353 @@
|
|||||||
|
package replay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type simdecoder struct {
|
||||||
|
r *gzip.Reader
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func readfull(r io.Reader, b []byte) error {
|
||||||
|
_, err := io.ReadFull(r, b)
|
||||||
|
if err == io.EOF {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *simdecoder) f(name string) float32 {
|
||||||
|
var b [4]byte
|
||||||
|
err := readfull(r.r, b[:])
|
||||||
|
switch err {
|
||||||
|
case nil: // do nothing
|
||||||
|
case io.ErrUnexpectedEOF:
|
||||||
|
// Check that the existing error doesn't already have this one
|
||||||
|
// so we don't spam on short input.
|
||||||
|
if r.err != nil && errors.Is(r.err, err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
r.err = errors.Join(r.err, fmt.Errorf("scanning %s: %w", name, err))
|
||||||
|
}
|
||||||
|
return math.Float32frombits(binary.LittleEndian.Uint32(b[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *simdecoder) i(name string) int32 {
|
||||||
|
var b [4]byte
|
||||||
|
err := readfull(r.r, b[:])
|
||||||
|
switch err {
|
||||||
|
case nil: // do nothing
|
||||||
|
case io.ErrUnexpectedEOF:
|
||||||
|
// Check that the existing error doesn't already have this one
|
||||||
|
// so we don't spam on short input.
|
||||||
|
if r.err != nil && errors.Is(r.err, err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
r.err = errors.Join(r.err, fmt.Errorf("scanning %s: %w", name, err))
|
||||||
|
}
|
||||||
|
return int32(binary.LittleEndian.Uint32(b[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *simdecoder) i16(name string) int16 {
|
||||||
|
var b [2]byte
|
||||||
|
err := readfull(r.r, b[:])
|
||||||
|
switch err {
|
||||||
|
case nil: // do nothing
|
||||||
|
case io.ErrUnexpectedEOF:
|
||||||
|
// Check that the existing error doesn't already have this one
|
||||||
|
// so we don't spam on short input.
|
||||||
|
if r.err != nil && errors.Is(r.err, err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
r.err = errors.Join(r.err, fmt.Errorf("scanning %s: %w", name, err))
|
||||||
|
}
|
||||||
|
return int16(binary.LittleEndian.Uint16(b[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *simdecoder) i8(name string) int8 {
|
||||||
|
var b [1]byte
|
||||||
|
err := readfull(r.r, b[:])
|
||||||
|
switch err {
|
||||||
|
case nil: // do nothing
|
||||||
|
case io.ErrUnexpectedEOF:
|
||||||
|
// Check that the existing error doesn't already have this one
|
||||||
|
// so we don't spam on short input.
|
||||||
|
if r.err != nil && errors.Is(r.err, err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
r.err = errors.Join(r.err, fmt.Errorf("scanning %s: %w", name, err))
|
||||||
|
}
|
||||||
|
return int8(b[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeSim(simDataBase64 string) (*RaceSimulateData, error) {
|
||||||
|
r, err := gzip.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(simDataBase64)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d := simdecoder{r: r}
|
||||||
|
|
||||||
|
s := RaceSimulateData{
|
||||||
|
Header: RaceSimulateDataHeader{
|
||||||
|
MaxLength: d.i("RaceSimulateDataHeader.maxLength"),
|
||||||
|
Version: d.i("RaceSimulateDataHeader.version"),
|
||||||
|
},
|
||||||
|
DistanceDiffMax: d.f("distanceDiffMax"),
|
||||||
|
HorseNum: d.i("horseNum"),
|
||||||
|
HorseFrameSize: d.i("horseFrameSize"),
|
||||||
|
HorseResultSize: d.i("horseResultSize"),
|
||||||
|
PaddingSize1: d.i("PaddingSize1"),
|
||||||
|
FrameCount: d.i("frameCount"),
|
||||||
|
FrameSize: d.i("frameSize"),
|
||||||
|
}
|
||||||
|
s.Frame = make([]RaceSimulateFrameData, s.FrameCount)
|
||||||
|
horseframes := make([]RaceSimulateHorseFrameData, s.FrameCount*s.HorseNum)
|
||||||
|
for i := range s.Frame {
|
||||||
|
s.Frame[i] = RaceSimulateFrameData{
|
||||||
|
Time: d.f("RaceSimulateFrameData.time"),
|
||||||
|
HorseFrame: horseframes[i*int(s.HorseNum) : (i+1)*int(s.HorseNum)],
|
||||||
|
}
|
||||||
|
for j := range s.Frame[i].HorseFrame {
|
||||||
|
s.Frame[i].HorseFrame[j] = RaceSimulateHorseFrameData{
|
||||||
|
Distance: d.f("RaceSimulateHorseFrameData.distance"),
|
||||||
|
LanePosition: d.i16("RaceSimulateHorseFrameData.lanePosition"),
|
||||||
|
Speed: d.i16("RaceSimulateHorseFrameData.speed"),
|
||||||
|
HP: d.i16("RaceSimulateHorseFrameData.hp"),
|
||||||
|
TemptationMode: TemptationMode(d.i8("RaceSimulateHorseFrameData.temptationMode")),
|
||||||
|
BlockFrontHorseIndex: d.i8("RaceSimulateHorseFrameData.blockFrontHorseIndex"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.PaddingSize2 = d.i("PaddingSize2")
|
||||||
|
s.HorseResult = make([]RaceSimulateHorseResultData, s.HorseNum)
|
||||||
|
for i := range s.HorseResult {
|
||||||
|
s.HorseResult[i] = RaceSimulateHorseResultData{
|
||||||
|
FinishOrder: d.i("RaceSimulateHorseResultData.finishOrder"),
|
||||||
|
FinishTime: d.f("RaceSimulateHorseResultData.finishTime"),
|
||||||
|
FinishDiffTime: d.f("RaceSimulateHorseResultData.finishDiffTime"),
|
||||||
|
StartDelayTime: d.f("RaceSimulateHorseResultData.startDelayTime"),
|
||||||
|
GutsOrder: d.i8("RaceSimulateHorseResultData.gutsOrder"),
|
||||||
|
WizOrder: d.i8("RaceSimulateHorseResultData.wizOrder"),
|
||||||
|
LastSpurtStartDistance: d.f("RaceSimulateHorseResultData.lastSpurtStartDistance"),
|
||||||
|
RunningStyle: RunningStyle(d.i8("RaceSimulateHorseResultData.runningStyle")),
|
||||||
|
Defeat: d.i("RaceSimulateHorseResultData.defeat"),
|
||||||
|
FinishTimeRaw: d.f("RaceSimulateHorseResultData.finishTimeRaw"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.PaddingSize3 = d.i("PaddingSize3")
|
||||||
|
s.EventCount = d.i("eventCount")
|
||||||
|
s.Event = make([]SizedEvent, s.EventCount)
|
||||||
|
for i := range s.Event {
|
||||||
|
s.Event[i] = SizedEvent{
|
||||||
|
EventSize: d.i16("SizedEvent.eventSize"),
|
||||||
|
Event: RaceSimulateEventData{
|
||||||
|
FrameTime: d.f("RaceSimulateEventData.frameTime"),
|
||||||
|
Type: SimulateEventType(d.i8("RaceSimulateEventData.type")),
|
||||||
|
ParamCount: d.i8("RaceSimulateEventData.paramCount"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s.Event[i].Event.Param = make([]int32, s.Event[i].Event.ParamCount)
|
||||||
|
for j := range s.Event[i].Event.Param {
|
||||||
|
s.Event[i].Event.Param[j] = d.i("RaceSimulateEventData.param")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.err != nil {
|
||||||
|
return nil, d.err
|
||||||
|
}
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RaceSimulateData struct {
|
||||||
|
Header RaceSimulateDataHeader `json:"header"`
|
||||||
|
DistanceDiffMax float32 `json:"distanceDiffMax"`
|
||||||
|
HorseNum int32 `json:"horseNum"`
|
||||||
|
HorseFrameSize int32 `json:"horseFrameSize"`
|
||||||
|
HorseResultSize int32 `json:"horseResultSize"`
|
||||||
|
PaddingSize1 int32 `json:"PaddingSize1"`
|
||||||
|
FrameCount int32 `json:"frameCount"`
|
||||||
|
FrameSize int32 `json:"frameSize"`
|
||||||
|
Frame []RaceSimulateFrameData `json:"frame"`
|
||||||
|
PaddingSize2 int32 `json:"PaddingSize2"`
|
||||||
|
HorseResult []RaceSimulateHorseResultData `json:"horseResult"`
|
||||||
|
PaddingSize3 int32 `json:"PaddingSize3"`
|
||||||
|
EventCount int32 `json:"eventCount"`
|
||||||
|
Event []SizedEvent `json:"event"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RaceSimulateDataHeader struct {
|
||||||
|
MaxLength int32
|
||||||
|
Version int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type RaceSimulateFrameData struct {
|
||||||
|
Time float32 `json:"time"`
|
||||||
|
HorseFrame []RaceSimulateHorseFrameData `json:"horseFrame"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RaceSimulateHorseFrameData struct {
|
||||||
|
Distance float32 `json:"distance"`
|
||||||
|
LanePosition int16 `json:"lanePosition"`
|
||||||
|
Speed int16 `json:"speed"`
|
||||||
|
HP int16 `json:"hp"`
|
||||||
|
TemptationMode TemptationMode `json:"temptationMode"`
|
||||||
|
BlockFrontHorseIndex int8 `json:"blockFrontHorseIndex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TemptationMode int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type TemptationMode -linecomment
|
||||||
|
const (
|
||||||
|
TemptationNull TemptationMode = iota // NULL
|
||||||
|
TemptationPositionSashi // POSITION_SASHI
|
||||||
|
TemptationPositionSenko // POSITION_SENKO
|
||||||
|
TemptationPositionNige // POSITION_NIGE
|
||||||
|
TemptationBoost // BOOST
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t TemptationMode) AppendText(b []byte) ([]byte, error) {
|
||||||
|
return append(b, t.String()...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TemptationMode) MarshalText() ([]byte, error) {
|
||||||
|
return t.AppendText(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TemptationMode) UnmarshalText(b []byte) error {
|
||||||
|
switch string(b) {
|
||||||
|
case "NULL":
|
||||||
|
*t = TemptationNull
|
||||||
|
case "POSITION_SASHI":
|
||||||
|
*t = TemptationPositionSashi
|
||||||
|
case "POSITION_SENKO":
|
||||||
|
*t = TemptationPositionSenko
|
||||||
|
case "POSITION_NIGE":
|
||||||
|
*t = TemptationPositionNige
|
||||||
|
case "BOOST":
|
||||||
|
*t = TemptationBoost
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid TemptationMode %q", b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RaceSimulateHorseResultData struct {
|
||||||
|
FinishOrder int32 `json:"finishOrder"`
|
||||||
|
FinishTime float32 `json:"finishTime"`
|
||||||
|
FinishDiffTime float32 `json:"finishDiffTime"`
|
||||||
|
StartDelayTime float32 `json:"startDelayTime"`
|
||||||
|
GutsOrder int8 `json:"gutsOrder"`
|
||||||
|
WizOrder int8 `json:"wizOrder"`
|
||||||
|
LastSpurtStartDistance float32 `json:"lastSpurtStartDistance"`
|
||||||
|
RunningStyle RunningStyle `json:"runningStyle"`
|
||||||
|
Defeat int32 `json:"defeat"`
|
||||||
|
FinishTimeRaw float32 `json:"finishTimeRaw"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunningStyle int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type RunningStyle -linecomment
|
||||||
|
const (
|
||||||
|
StyleNone RunningStyle = iota // NONE
|
||||||
|
StyleNige // NIGE
|
||||||
|
StyleSenko // SENKO
|
||||||
|
StyleSashi // SASHI
|
||||||
|
StyleOikomi // OIKOMI
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s RunningStyle) AppendText(b []byte) ([]byte, error) {
|
||||||
|
return append(b, s.String()...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s RunningStyle) MarshalText() ([]byte, error) {
|
||||||
|
return s.AppendText(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RunningStyle) UnmarshalText(b []byte) error {
|
||||||
|
switch string(b) {
|
||||||
|
case "NONE":
|
||||||
|
*s = StyleNone
|
||||||
|
case "NIGE":
|
||||||
|
*s = StyleNige
|
||||||
|
case "SENKO":
|
||||||
|
*s = StyleSenko
|
||||||
|
case "SASHI":
|
||||||
|
*s = StyleSashi
|
||||||
|
case "OIKOMI":
|
||||||
|
*s = StyleOikomi
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid RunningStyle %q", b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SizedEvent struct {
|
||||||
|
EventSize int16 `json:"eventSize"`
|
||||||
|
Event RaceSimulateEventData `json:"event"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RaceSimulateEventData struct {
|
||||||
|
FrameTime float32 `json:"frameTime"`
|
||||||
|
Type SimulateEventType `json:"type"`
|
||||||
|
ParamCount int8 `json:"paramCount"`
|
||||||
|
Param []int32 `json:"param"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimulateEventType int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type SimulateEventType -linecomment
|
||||||
|
const (
|
||||||
|
EventScore SimulateEventType = iota // SCORE
|
||||||
|
EventChallengeMatchPoint // CHALLENGE_MATCH_POINT
|
||||||
|
EventNoUse2 // NOUSE_2
|
||||||
|
EventSkill // SKILL
|
||||||
|
EventCompeteTop // COMPETE_TOP
|
||||||
|
EventCompeteFight // COMPETE_FIGHT
|
||||||
|
EventReleaseConservePower // RELEASE_CONSERVE_POWER
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t SimulateEventType) AppendText(b []byte) ([]byte, error) {
|
||||||
|
return append(b, t.String()...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t SimulateEventType) MarshalText() ([]byte, error) {
|
||||||
|
return t.AppendText(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SimulateEventType) UnmarshalText(b []byte) error {
|
||||||
|
switch string(b) {
|
||||||
|
case "SCORE":
|
||||||
|
*t = EventScore
|
||||||
|
case "CHALLENGE_MATCH_POINT":
|
||||||
|
*t = EventChallengeMatchPoint
|
||||||
|
case "NOUSE_2":
|
||||||
|
*t = EventNoUse2
|
||||||
|
case "SKILL":
|
||||||
|
*t = EventSkill
|
||||||
|
case "COMPETE_TOP":
|
||||||
|
*t = EventCompeteTop
|
||||||
|
case "COMPETE_FIGHT":
|
||||||
|
*t = EventCompeteFight
|
||||||
|
case "RELEASE_CONSERVE_POWER":
|
||||||
|
*t = EventReleaseConservePower
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid SimulateEventType %q", b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,30 @@
|
|||||||
|
// Code generated by "stringer -type SimulateEventType -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package replay
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[EventScore-0]
|
||||||
|
_ = x[EventChallengeMatchPoint-1]
|
||||||
|
_ = x[EventNoUse2-2]
|
||||||
|
_ = x[EventSkill-3]
|
||||||
|
_ = x[EventCompeteTop-4]
|
||||||
|
_ = x[EventCompeteFight-5]
|
||||||
|
_ = x[EventReleaseConservePower-6]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _SimulateEventType_name = "SCORECHALLENGE_MATCH_POINTNOUSE_2SKILLCOMPETE_TOPCOMPETE_FIGHTRELEASE_CONSERVE_POWER"
|
||||||
|
|
||||||
|
var _SimulateEventType_index = [...]uint8{0, 5, 26, 33, 38, 49, 62, 84}
|
||||||
|
|
||||||
|
func (i SimulateEventType) String() string {
|
||||||
|
idx := int(i) - 0
|
||||||
|
if i < 0 || idx >= len(_SimulateEventType_index)-1 {
|
||||||
|
return "SimulateEventType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _SimulateEventType_name[_SimulateEventType_index[idx]:_SimulateEventType_index[idx+1]]
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Code generated by "stringer -type TemptationMode -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package replay
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[TemptationNull-0]
|
||||||
|
_ = x[TemptationPositionSashi-1]
|
||||||
|
_ = x[TemptationPositionSenko-2]
|
||||||
|
_ = x[TemptationPositionNige-3]
|
||||||
|
_ = x[TemptationBoost-4]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _TemptationMode_name = "NULLPOSITION_SASHIPOSITION_SENKOPOSITION_NIGEBOOST"
|
||||||
|
|
||||||
|
var _TemptationMode_index = [...]uint8{0, 4, 18, 32, 45, 50}
|
||||||
|
|
||||||
|
func (i TemptationMode) String() string {
|
||||||
|
idx := int(i) - 0
|
||||||
|
if i < 0 || idx >= len(_TemptationMode_index)-1 {
|
||||||
|
return "TemptationMode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _TemptationMode_name[_TemptationMode_index[idx]:_TemptationMode_index[idx+1]]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user