diff --git a/internal/sdkserver/api.go b/internal/sdkserver/api.go index 0bda681..2176d66 100644 --- a/internal/sdkserver/api.go +++ b/internal/sdkserver/api.go @@ -47,6 +47,7 @@ func registerAPIRoutes(mux *http.ServeMux) { mux.HandleFunc("/api/teleport", corsWrapper(apiTeleport)) mux.HandleFunc("/api/gachabase/fetch", corsWrapper(apiGachabaseFetch)) mux.HandleFunc("/api/command", corsWrapper(apiCommand)) + mux.HandleFunc("/api/data-in-game", corsWrapper(apiDataInGame)) } func apiRoot(w http.ResponseWriter, r *http.Request) { @@ -340,6 +341,60 @@ func apiPersistentData(w http.ResponseWriter, r *http.Request) { } } +// apiDataInGame handles full read/write of "data-in-game.json" file for Firefly compatibility +func apiDataInGame(w http.ResponseWriter, r *http.Request) { + apiMu.Lock() + defer apiMu.Unlock() + + switch r.Method { + case http.MethodGet: + raw, err := os.ReadFile("data-in-game.json") + if err != nil { + writeJSON(w, map[string]any{"error": err.Error(), "status": 404}) + return + } + var parsed any + if err := json.Unmarshal(raw, &parsed); err != nil { + writeJSON(w, map[string]any{"error": err.Error(), "status": 500}) + return + } + writeJSON(w, map[string]any{"status": 200, "data": parsed}) + + case http.MethodPost, http.MethodPut: + body, err := io.ReadAll(r.Body) + if err != nil || len(body) == 0 { + writeJSON(w, map[string]any{"error": "empty body", "status": 400}) + return + } + + var parsed map[string]any + if err := json.Unmarshal(body, &parsed); err != nil { + writeJSON(w, map[string]any{"error": "invalid json: " + err.Error(), "status": 400}) + return + } + + // Merge into existing data-in-game.json if present + existing := make(map[string]any) + if raw, err := os.ReadFile("data-in-game.json"); err == nil { + _ = json.Unmarshal(raw, &existing) + } + for k, v := range parsed { + existing[k] = v + } + + out, _ := json.MarshalIndent(existing, "", " ") + if err := os.WriteFile("data-in-game.json", out, 0o644); err != nil { + writeJSON(w, map[string]any{"error": "failed to write data-in-game.json: " + err.Error(), "status": 500}) + return + } + + writeJSON(w, map[string]any{"message": "data-in-game.json saved successfully", "status": 200}) + + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + } +} + // apiExRankToggle toggles Ex-Rank state for characters func apiExRankToggle(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost {