package main import ( "context" _ "embed" "errors" "fmt" "log/slog" "zombiezen.com/go/sqlite" "zombiezen.com/go/sqlite/sqlitemigration" "zombiezen.com/go/sqlite/sqlitex" "git.sunturtle.xyz/zephyr/horse/umadump/ism" ) const appID int32 = 0x4acedb //go:embed schema-00.sql var schema0 string //go:embed schema-final.sql var schemaFinal string func opendb(uri, journalMode string) *sqlitemigration.Pool { schema := sqlitemigration.Schema{ Migrations: []string{ schema0, }, AppID: appID, RepeatableMigration: schemaFinal, } j := "PRAGMA journal_mode=" + journalMode opts := sqlitemigration.Options{ Flags: sqlite.OpenReadWrite | sqlite.OpenCreate | sqlite.OpenURI, PrepareConn: func(conn *sqlite.Conn) error { if journalMode == "" { return nil } stmt, _, err := conn.PrepareTransient(j) if err != nil { return err } _, err = stmt.Step() return errors.Join(err, stmt.Finalize()) }, } db := sqlitemigration.NewPool(uri, schema, opts) return db } func insert(ctx context.Context, db *sqlitemigration.Pool, log *slog.Logger, career *ism.WorkIdleSingleMode, trainerID int64) (err error) { conn, err := db.Take(ctx) if err != nil { return err } defer db.Put(conn) defer sqlitex.Transaction(conn)(&err) } //go:embed insert-career.sql var insertCareerSQL string func insertCareer(conn *sqlite.Conn, career *ism.WorkIdleSingleMode, trainerID int64) error { stmt := conn.Prep(insertCareerSQL) stmt.SetInt64("$trainer_id", trainerID) stmt.SetInt64("$trained_chara_id", int64(career.End.Chara.SingleModeCharaID)) stmt.SetInt64("$card_id", int64(career.End.Chara.CardID)) stmt.SetInt64("$scenario_id", int64(career.End.Chara.ScenarioID)) stmt.SetInt64("$route_id", int64(career.End.Chara.RouteID)) stmt.SetText("$start_time", career.ProgressInfo.StartTime) stmt.SetText("$end_time", career.ProgressInfo.EndTime) stmt.SetInt64("$chara_grade", int64(career.End.Chara.CharaGrade)) stmt.SetInt64("$star_level", int64(career.End.Chara.Rarity)) stmt.SetInt64("$potential_level", int64(career.End.Chara.TalentLevel)) stmt.SetInt64("$speed", int64(career.End.Chara.Speed)) stmt.SetInt64("$stamina", int64(career.End.Chara.Stamina)) stmt.SetInt64("$power", int64(career.End.Chara.Power)) stmt.SetInt64("$guts", int64(career.End.Chara.Guts)) stmt.SetInt64("$wit", int64(career.End.Chara.Wiz)) stmt.SetInt64("$energy", int64(career.End.Chara.Vital)) stmt.SetInt64("$max_speed", int64(career.End.Chara.MaxSpeed)) stmt.SetInt64("$max_stamina", int64(career.End.Chara.MaxStamina)) stmt.SetInt64("$max_power", int64(career.End.Chara.MaxPower)) stmt.SetInt64("$max_guts", int64(career.End.Chara.MaxGuts)) stmt.SetInt64("$max_wit", int64(career.End.Chara.MaxWiz)) stmt.SetInt64("$max_energy", int64(career.End.Chara.MaxVital)) stmt.SetInt64("$skill_point", int64(career.ProgressLog.TotalSkillPoint)) stmt.SetInt64("$mood", int64(career.End.Chara.Motivation)) stmt.SetInt64("$turf", int64(career.End.Chara.ProperGroundTurf)) stmt.SetInt64("$dirt", int64(career.End.Chara.ProperGroundDirt)) stmt.SetInt64("$sprint", int64(career.End.Chara.ProperDistanceShort)) stmt.SetInt64("$mile", int64(career.End.Chara.ProperDistanceMile)) stmt.SetInt64("$medium", int64(career.End.Chara.ProperDistanceMiddle)) stmt.SetInt64("$long", int64(career.End.Chara.ProperDistanceLong)) stmt.SetInt64("$front_runner", int64(career.End.Chara.ProperRunningStyleNige)) stmt.SetInt64("$pace_chaser", int64(career.End.Chara.ProperRunningStyleSenko)) stmt.SetInt64("$late_surger", int64(career.End.Chara.ProperRunningStyleSashi)) stmt.SetInt64("$end_closer", int64(career.End.Chara.ProperRunningStyleOikomi)) stmt.SetInt64("$start_speed", int64(career.ProgressInfo.Chara.Speed)) stmt.SetInt64("$start_stamina", int64(career.ProgressInfo.Chara.Stamina)) stmt.SetInt64("$start_power", int64(career.ProgressInfo.Chara.Power)) stmt.SetInt64("$start_guts", int64(career.ProgressInfo.Chara.Guts)) stmt.SetInt64("$start_wit", int64(career.ProgressInfo.Chara.Wiz)) stmt.SetInt64("$start_max_speed", int64(career.ProgressInfo.Chara.MaxSpeed)) stmt.SetInt64("$start_max_stamina", int64(career.ProgressInfo.Chara.MaxStamina)) stmt.SetInt64("$start_max_power", int64(career.ProgressInfo.Chara.MaxPower)) stmt.SetInt64("$start_max_guts", int64(career.ProgressInfo.Chara.MaxGuts)) stmt.SetInt64("$start_max_wit", int64(career.ProgressInfo.Chara.MaxWiz)) stmt.SetInt64("$start_turf", int64(career.ProgressInfo.Chara.ProperGroundTurf)) stmt.SetInt64("$start_dirt", int64(career.ProgressInfo.Chara.ProperGroundDirt)) stmt.SetInt64("$start_sprint", int64(career.ProgressInfo.Chara.ProperDistanceShort)) stmt.SetInt64("$start_mile", int64(career.ProgressInfo.Chara.ProperDistanceMile)) stmt.SetInt64("$start_medium", int64(career.ProgressInfo.Chara.ProperDistanceMiddle)) stmt.SetInt64("$start_long", int64(career.ProgressInfo.Chara.ProperDistanceLong)) stmt.SetInt64("$start_front_runner", int64(career.ProgressInfo.Chara.ProperRunningStyleNige)) stmt.SetInt64("$start_pace_chaser", int64(career.ProgressInfo.Chara.ProperRunningStyleSenko)) stmt.SetInt64("$start_late_surger", int64(career.ProgressInfo.Chara.ProperRunningStyleSashi)) stmt.SetInt64("$start_end_closer", int64(career.ProgressInfo.Chara.ProperRunningStyleOikomi)) stmt.SetInt64("$fans", int64(career.End.Chara.Fans)) stmt.SetInt64("$race_program_id", int64(career.End.Chara.RaceProgramID)) stmt.SetInt64("$reserve_race_program_id", int64(career.End.Chara.ReserveRaceProgramID)) stmt.SetInt64("$race_running_style", int64(career.End.Chara.RaceRunningStyle)) stmt.SetInt64("$is_short_race", int64(career.End.Chara.IsShortRace)) stmt.SetInt64("$succession_trained_chara_id_1", int64(career.OldChara.SuccessionTrainedCharaID1)) stmt.SetInt64("$succession_trained_chara_id_2", int64(career.OldChara.SuccessionTrainedCharaID2)) _, err := stmt.Step() err = errors.Join(err, stmt.Reset()) if err != nil { return fmt.Errorf("inserting race course: %w", err) } return nil } func insertConditions(conn *sqlite.Conn, cs []ism.CharaEffectLog, trainerID int64, trainedChara int32) error { const sql = `INSERT INTO condition VALUES (?, ?, ?, ?)` stmt := conn.Prep(sql) stmt.BindInt64(1, trainerID) stmt.BindInt64(2, int64(trainedChara)) for _, c := range cs { stmt.BindInt64(3, int64(c.CharaEffectID)) stmt.BindBool(4, c.IsActive) _, err := stmt.Step() err = errors.Join(err, stmt.Reset()) if err != nil { return fmt.Errorf("inserting condition: %w", err) } } return nil } //go:embed insert-gain.sql var insertGainSQL string func insertGain(conn *sqlite.Conn, gain *ism.IdleSingleModeGainInfo, trainerID int64, trainedChara int32, source int32) error { stmt := conn.Prep(insertGainSQL) stmt.SetInt64("$trainer_id", trainerID) stmt.SetInt64("$trained_chara_id", int64(trainedChara)) stmt.SetInt64("$source", int64(source)) stmt.SetInt64("$gain_speed", int64(gain.Speed.Value)) stmt.SetInt64("$gain_stamina", int64(gain.Stamina.Value)) stmt.SetInt64("$gain_power", int64(gain.Power.Value)) stmt.SetInt64("$gain_guts", int64(gain.Guts.Value)) stmt.SetInt64("$gain_wit", int64(gain.Wiz.Value)) stmt.SetInt64("$gain_skill_point", int64(gain.SkillPoint)) stmt.SetInt64("$gain_max_speed", int64(gain.MaxSpeed)) stmt.SetInt64("$gain_max_stamina", int64(gain.MaxStamina)) stmt.SetInt64("$gain_max_power", int64(gain.MaxPower)) stmt.SetInt64("$gain_max_guts", int64(gain.MaxGuts)) stmt.SetInt64("$gain_max_wit", int64(gain.MaxWiz)) stmt.SetInt64("$gain_turf", int64(gain.ProperGroundTurf)) stmt.SetInt64("$gain_dirt", int64(gain.ProperGroundDirt)) stmt.SetInt64("$gain_sprint", int64(gain.ProperDistanceShort)) stmt.SetInt64("$gain_mile", int64(gain.ProperDistanceMile)) stmt.SetInt64("$gain_medium", int64(gain.ProperDistanceMiddle)) stmt.SetInt64("$gain_long", int64(gain.ProperDistanceLong)) stmt.SetInt64("$gain_front_runner", int64(gain.ProperRunningStyleNige)) stmt.SetInt64("$gain_pace_chaser", int64(gain.ProperRunningStyleSenko)) stmt.SetInt64("$gain_late_surger", int64(gain.ProperRunningStyleSashi)) stmt.SetInt64("$gain_end_closer", int64(gain.ProperRunningStyleOikomi)) _, err := stmt.Step() err = errors.Join(err, stmt.Reset()) if err != nil { return fmt.Errorf("inserting gain info: %w", err) } return nil }