added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / api.c
index 6fd07dce6fa1a9ecf2eeb6d437f0e1092f6cfcfc..3e2b387e88ea483dfd600fe294bcdbf3e7c984b4 100644 (file)
--- a/src/api.c
+++ b/src/api.c
@@ -635,6 +635,234 @@ void ApiGetScoreAsThread(int nr)
 }
 
 
+// ============================================================================
+// get score tape API functions
+// ============================================================================
+
+struct ApiGetScoreTapeThreadData
+{
+  int level_nr;
+  int score_id;
+  char *score_tape_filename;
+};
+
+static void *CreateThreadData_ApiGetScoreTape(int nr, int id,
+                                             char *score_tape_basename)
+{
+  struct ApiGetScoreTapeThreadData *data =
+    checked_malloc(sizeof(struct ApiGetScoreTapeThreadData));
+  char *score_tape_filename = getScoreCacheTapeFilename(score_tape_basename, nr);
+
+  data->level_nr = nr;
+  data->score_id = id;
+  data->score_tape_filename = getStringCopy(score_tape_filename);
+
+  return data;
+}
+
+static void FreeThreadData_ApiGetScoreTape(void *data_raw)
+{
+  struct ApiGetScoreTapeThreadData *data = data_raw;
+
+  checked_free(data->score_tape_filename);
+  checked_free(data);
+}
+
+static boolean SetRequest_ApiGetScoreTape(struct HttpRequest *request,
+                                          void *data_raw)
+{
+  struct ApiGetScoreTapeThreadData *data = data_raw;
+  int score_id = data->score_id;
+
+  request->hostname = setup.api_server_hostname;
+  request->port     = API_SERVER_PORT;
+  request->method   = API_SERVER_METHOD;
+  request->uri      = API_SERVER_URI_GETTAPE;
+
+  snprintf(request->body, MAX_HTTP_BODY_SIZE,
+          "{\n"
+          "%s"
+          "  \"game_version\":         \"%s\",\n"
+          "  \"game_platform\":        \"%s\",\n"
+          "  \"id\":                   \"%d\"\n"
+          "}\n",
+          getPasswordJSON(setup.api_server_password),
+          getProgramRealVersionString(),
+          getProgramPlatformString(),
+          score_id);
+
+  ConvertHttpRequestBodyToServerEncoding(request);
+
+  return TRUE;
+}
+
+static void HandleResponse_ApiGetScoreTape(struct HttpResponse *response,
+                                           void *data_raw)
+{
+  struct ApiGetScoreTapeThreadData *data = data_raw;
+
+  if (response->body_size == 0)
+  {
+    // no score tape available for this level
+
+    return;
+  }
+
+  // (do not convert HTTP response body, as it contains binary data here)
+
+  int level_nr = data->level_nr;
+  char *filename = data->score_tape_filename;
+  FILE *file;
+  int i;
+
+  // used instead of "leveldir_current->subdir" (for network games)
+  InitScoreCacheTapeDirectory(levelset.identifier, level_nr);
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Warn("cannot save score tape file '%s'", filename);
+
+    return;
+  }
+
+  for (i = 0; i < response->body_size; i++)
+    fputc(response->body[i], file);
+
+  fclose(file);
+
+  SetFilePermissions(filename, PERMS_PRIVATE);
+
+  server_scores.tape_downloaded = TRUE;
+}
+
+#if defined(PLATFORM_EMSCRIPTEN)
+static void Emscripten_ApiGetScoreTape_Loaded(unsigned handle, void *data_raw,
+                                              void *buffer, unsigned int size)
+{
+  struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
+
+  if (response != NULL)
+  {
+    HandleResponse_ApiGetScoreTape(response, data_raw);
+
+    checked_free(response);
+  }
+  else
+  {
+    Error("server response too large to handle (%d bytes)", size);
+  }
+
+  FreeThreadData_ApiGetScoreTape(data_raw);
+}
+
+static void Emscripten_ApiGetScoreTape_Failed(unsigned handle, void *data_raw,
+                                              int code, const char *status)
+{
+  Error("server failed to handle request: %d %s", code, status);
+
+  FreeThreadData_ApiGetScoreTape(data_raw);
+}
+
+static void Emscripten_ApiGetScoreTape_Progress(unsigned handle, void *data_raw,
+                                                int bytes, int size)
+{
+  // nothing to do here
+}
+
+static void Emscripten_ApiGetScoreTape_HttpRequest(struct HttpRequest *request,
+                                                   void *data_raw)
+{
+  if (!SetRequest_ApiGetScoreTape(request, data_raw))
+  {
+    FreeThreadData_ApiGetScoreTape(data_raw);
+
+    return;
+  }
+
+  emscripten_async_wget2_data(request->uri,
+                             request->method,
+                             request->body,
+                             data_raw,
+                             TRUE,
+                             Emscripten_ApiGetScoreTape_Loaded,
+                             Emscripten_ApiGetScoreTape_Failed,
+                             Emscripten_ApiGetScoreTape_Progress);
+}
+
+#else
+
+static void ApiGetScoreTape_HttpRequestExt(struct HttpRequest *request,
+                                           struct HttpResponse *response,
+                                           void *data_raw)
+{
+  if (!SetRequest_ApiGetScoreTape(request, data_raw))
+    return;
+
+  if (!DoHttpRequest(request, response))
+  {
+    Error("HTTP request failed: %s", GetHttpError());
+
+    return;
+  }
+
+  if (!HTTP_SUCCESS(response->status_code))
+  {
+    // do not show error message if no scores found for this level set
+    if (response->status_code == 404)
+      return;
+
+    Error("server failed to handle request: %d %s",
+         response->status_code,
+         response->status_text);
+
+    return;
+  }
+
+  HandleResponse_ApiGetScoreTape(response, data_raw);
+}
+
+static void ApiGetScoreTape_HttpRequest(struct HttpRequest *request,
+                                        struct HttpResponse *response,
+                                        void *data_raw)
+{
+  ApiGetScoreTape_HttpRequestExt(request, response, data_raw);
+
+  FreeThreadData_ApiGetScoreTape(data_raw);
+}
+#endif
+
+static int ApiGetScoreTapeThread(void *data_raw)
+{
+  struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
+  struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
+
+  program.api_thread_count++;
+
+#if defined(PLATFORM_EMSCRIPTEN)
+  Emscripten_ApiGetScoreTape_HttpRequest(request, data_raw);
+#else
+  ApiGetScoreTape_HttpRequest(request, response, data_raw);
+#endif
+
+  program.api_thread_count--;
+
+  checked_free(request);
+  checked_free(response);
+
+  return 0;
+}
+
+void ApiGetScoreTapeAsThread(int nr, int id, char *score_tape_basename)
+{
+  struct ApiGetScoreTapeThreadData *data =
+    CreateThreadData_ApiGetScoreTape(nr, id, score_tape_basename);
+
+  ExecuteAsThread(ApiGetScoreTapeThread,
+                 "ApiGetScoreTape", data,
+                 "download score tape from server");
+}
+
+
 // ============================================================================
 // rename player API functions
 // ============================================================================