package sqex import ( "fmt" "strings" ) // PatchEntry is one row of Square Enix's patch list: the version a client // reaches after applying it, and where to download it from. type PatchEntry struct { VersionID string URL string } // ParsePatchList parses the body SE's version server returns when the client // needs updating (see auth.RegisterSession). The format mirrors what // FFXIVQuickLauncher's PatchListParser expects: five header lines, then one // tab-separated row per patch — length, three reserved fields, version id, // hash type and either the URL (6 fields) or block size + hashes + URL // (9 fields) — followed by two footer lines. func ParsePatchList(text string) ([]PatchEntry, error) { lines := strings.Split(text, "\n") for i, l := range lines { lines[i] = strings.TrimSuffix(l, "\r") } if len(lines) < 7 { return nil, fmt.Errorf("patch list too short (%d lines)", len(lines)) } var out []PatchEntry for _, line := range lines[5 : len(lines)-2] { if strings.TrimSpace(line) == "" { continue } f := strings.Split(line, "\t") if len(f) < 6 { return nil, fmt.Errorf("malformed patch list row: %q", line) } out = append(out, PatchEntry{VersionID: f[4], URL: f[len(f)-1]}) } return out, nil } // TargetVersion returns the version a client reaches after applying all // entries (the last one in list order), or "" for an empty list. func TargetVersion(entries []PatchEntry) string { if len(entries) == 0 { return "" } return entries[len(entries)-1].VersionID }