80 lines
2.6 KiB
Go
80 lines
2.6 KiB
Go
package gamedata
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// BattleDeckForDifficulty mirrors QuicklyQuestHelper.GetBattleDeckIdByDifficulty
|
|
// for the three story difficulties, and verifies the resulting design row.
|
|
// Already-selected difficulty decks retain their ID.
|
|
func BattleDeckForDifficulty(root, version string, packID int, deckID, difficulty uint64) (uint64, error) {
|
|
db, cleanup, err := openPackDatabase(root, version, packID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer cleanup()
|
|
return battleDeckForDifficultyFromDB(db, deckID, difficulty)
|
|
}
|
|
|
|
func openPackDatabase(root, version string, packID int) (*sql.DB, func(), error) {
|
|
if packID <= 0 {
|
|
return nil, nil, fmt.Errorf("gamedata: invalid battle pack %d", packID)
|
|
}
|
|
plain, err := ReadDatabase(root, version, fmt.Sprintf("pack%d", packID))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
dir, err := os.MkdirTemp("", "bd2-difficulty-")
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
path := filepath.Join(dir, "pack.db")
|
|
if err := os.WriteFile(path, plain, 0600); err != nil {
|
|
os.RemoveAll(dir)
|
|
return nil, nil, err
|
|
}
|
|
db, err := sql.Open("sqlite", "file:"+filepath.ToSlash(path)+"?mode=ro")
|
|
if err != nil {
|
|
os.RemoveAll(dir)
|
|
return nil, nil, err
|
|
}
|
|
return db, func() { db.Close(); os.RemoveAll(dir) }, nil
|
|
}
|
|
|
|
func battleDeckForDifficultyFromDB(db *sql.DB, deckID, difficulty uint64) (uint64, error) {
|
|
if deckID == 0 || deckID > 2147483647 || difficulty > 2 {
|
|
return 0, fmt.Errorf("gamedata: unsupported battle deck/difficulty %d/%d", deckID, difficulty)
|
|
}
|
|
var data []byte
|
|
if err := db.QueryRow("SELECT ProtoBuf FROM BattleDeckTable WHERE id=?", deckID).Scan(&data); err != nil {
|
|
return 0, fmt.Errorf("gamedata: battle deck %d: %w", deckID, err)
|
|
}
|
|
actual, err := phaseScalar(data, 24)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if actual == difficulty {
|
|
return deckID, nil
|
|
}
|
|
// Only an ordinary deck can be promoted. Changing a previously selected
|
|
// difficulty deck would conceal stale client quest state.
|
|
if actual != 0 || difficulty == 0 {
|
|
return 0, fmt.Errorf("gamedata: deck %d difficulty %d differs from selected %d", deckID, actual, difficulty)
|
|
}
|
|
selected := deckID + difficulty*100000
|
|
if err := db.QueryRow("SELECT ProtoBuf FROM BattleDeckTable WHERE id=?", selected).Scan(&data); err != nil {
|
|
return 0, fmt.Errorf("gamedata: difficulty deck %d: %w", selected, err)
|
|
}
|
|
actual, err = phaseScalar(data, 24)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if actual != difficulty {
|
|
return 0, fmt.Errorf("gamedata: difficulty deck %d has difficulty %d, want %d", selected, actual, difficulty)
|
|
}
|
|
return selected, nil
|
|
}
|