#include "tools.h"
#include "tape.h"
#include "config.h"
+#include "api.h"
#define ENABLE_UNUSED_CODE 0 // currently unused functions
#define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
// (element number only)
#define LEVEL_CHUNK_GRPX_UNCHANGED 2
+#define LEVEL_CHUNK_EMPX_UNCHANGED 2
#define LEVEL_CHUNK_NOTE_UNCHANGED 2
// (nothing at all if unchanged)
}
};
+static struct LevelFileConfigInfo chunk_config_EMPX[] =
+{
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &xx_ei.use_gfx_element, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1
+ }
+};
+
static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
{
{
*group = xx_group;
}
+
+ if (IS_EMPTY_ELEMENT(element) ||
+ IS_INTERNAL_ELEMENT(element))
+ {
+ xx_ei = *ei; // copy element data into temporary buffer
+
+ setConfigToDefaultsFromConfigList(chunk_config_EMPX);
+
+ *ei = xx_ei;
+ }
}
clipboard_elements_initialized = TRUE;
for (x = 0; x < 3; x++)
ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
+ // bits 0 - 31 of "has_event[]"
event_bits = getFile32BitBE(file);
- for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+ for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
if (event_bits & (1 << j))
ei->change->has_event[j] = TRUE;
return real_chunk_size;
}
+static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
+{
+ int element = getMappedElement(getFile16BitBE(file));
+ int real_chunk_size = 2;
+ struct ElementInfo *ei = &element_info[element];
+
+ xx_ei = *ei; // copy element data into temporary buffer
+
+ while (!checkEndOfFile(file))
+ {
+ real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
+ -1, element);
+
+ if (real_chunk_size >= chunk_size)
+ break;
+ }
+
+ *ei = xx_ei;
+
+ level->file_has_custom_elements = TRUE;
+
+ return real_chunk_size;
+}
+
static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
struct LevelFileInfo *level_file_info,
boolean level_info_only)
{ "NOTE", -1, LoadLevel_NOTE },
{ "CUSX", -1, LoadLevel_CUSX },
{ "GRPX", -1, LoadLevel_GRPX },
+ { "EMPX", -1, LoadLevel_EMPX },
{ NULL, 0, NULL }
};
{
// adjust level settings for (non-native) Sokoban-style levels
LoadLevel_InitSettings_SB(level);
+
+ // rename levels with title "nameless level" or if renaming is forced
+ if (leveldir_current->empty_level_name != NULL &&
+ (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
+ leveldir_current->force_level_name))
+ snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
+ leveldir_current->empty_level_name, level_nr);
}
static void LoadLevel_InitStandardElements(struct LevelInfo *level)
return chunk_size;
}
+static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
+{
+ struct ElementInfo *ei = &element_info[element];
+ int chunk_size = 0;
+ int i;
+
+ chunk_size += putFile16BitBE(file, element);
+
+ xx_ei = *ei; // copy element data into temporary buffer
+
+ for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
+ chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
+
+ return chunk_size;
+}
+
static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
boolean save_as_template)
{
SaveLevel_GRPX(file, level, element);
}
}
+
+ for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
+ {
+ int element = GET_EMPTY_ELEMENT(i);
+
+ chunk_size = SaveLevel_EMPX(NULL, level, element);
+ if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
+ {
+ putFileChunkBE(file, "EMPX", chunk_size);
+ SaveLevel_EMPX(file, level, element);
+ }
+ }
}
fclose(file);
Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
+ if (options.debug)
+ {
+ int i, j;
+
+ for (i = 0; i < NUM_ENVELOPES; i++)
+ {
+ char *text = level->envelope[i].text;
+ int text_len = strlen(text);
+ boolean has_text = FALSE;
+
+ for (j = 0; j < text_len; j++)
+ if (text[j] != ' ' && text[j] != '\n')
+ has_text = TRUE;
+
+ if (has_text)
+ {
+ Print("\n");
+ Print("Envelope %d:\n'%s'\n", i + 1, text);
+ }
+ }
+ }
+
PrintLine("-", 79);
}
CopyNativeTape_SP_to_RND(&level);
}
+void LoadScoreTape(char *score_tape_basename, int nr)
+{
+ char *filename = getScoreTapeFilename(score_tape_basename, nr);
+
+ LoadTapeFromFilename(filename);
+}
+
+void LoadScoreCacheTape(char *score_tape_basename, int nr)
+{
+ char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
+
+ LoadTapeFromFilename(filename);
+}
+
static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
{
// chunk required for team mode tapes with non-default screen size
char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
// used instead of "leveldir_current->subdir" (for network games)
- InitScoreDirectory(levelset.identifier);
+ InitScoreTapeDirectory(levelset.identifier, nr);
SaveTapeExt(filename);
}
strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
scores->entry[i].score = 0;
scores->entry[i].time = 0;
+
+ scores->entry[i].id = -1;
+ strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
+ strcpy(scores->entry[i].platform, UNKNOWN_NAME);
+ strcpy(scores->entry[i].version, UNKNOWN_NAME);
+ strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
+ strcpy(scores->entry[i].country_code, "??");
}
scores->num_entries = 0;
scores->updated = FALSE;
scores->uploaded = FALSE;
+ scores->tape_downloaded = FALSE;
scores->force_last_added = FALSE;
+
+ // The following values are intentionally not reset here:
+ // - last_level_nr
+ // - last_entry_nr
+ // - next_level_nr
+ // - continue_playing
+ // - continue_on_return
}
static void setScoreInfoToDefaults(void)
return chunk_size;
}
+static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
+{
+ int i;
+
+ for (i = 0; i < scores->num_entries; i++)
+ scores->entry[i].score = getFile32BitBE(file);
+
+ chunk_size = scores->num_entries * 4;
+
+ return chunk_size;
+}
+
static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
{
int i;
{ "INFO", -1, LoadScore_INFO },
{ "NAME", -1, LoadScore_NAME },
{ "SCOR", -1, LoadScore_SCOR },
+ { "SC4R", -1, LoadScore_SC4R },
{ "TIME", -1, LoadScore_TIME },
{ "TAPE", -1, LoadScore_TAPE },
putFile16BitBE(file, scores->entry[i].score);
}
+static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
+{
+ int i;
+
+ for (i = 0; i < scores->num_entries; i++)
+ putFile32BitBE(file, scores->entry[i].score);
+}
+
static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
{
int i;
int info_chunk_size;
int name_chunk_size;
int scor_chunk_size;
+ int sc4r_chunk_size;
int time_chunk_size;
int tape_chunk_size;
+ boolean has_large_score_values;
+ int i;
if (!(file = fopen(filename, MODE_WRITE)))
{
info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
scor_chunk_size = scores.num_entries * 2;
+ sc4r_chunk_size = scores.num_entries * 4;
time_chunk_size = scores.num_entries * 4;
tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
+ has_large_score_values = FALSE;
+ for (i = 0; i < scores.num_entries; i++)
+ if (scores.entry[i].score > 0xffff)
+ has_large_score_values = TRUE;
+
putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
putFileChunkBE(file, "NAME", name_chunk_size);
SaveScore_NAME(file, &scores);
- putFileChunkBE(file, "SCOR", scor_chunk_size);
- SaveScore_SCOR(file, &scores);
+ if (has_large_score_values)
+ {
+ putFileChunkBE(file, "SC4R", sc4r_chunk_size);
+ SaveScore_SC4R(file, &scores);
+ }
+ else
+ {
+ putFileChunkBE(file, "SCOR", scor_chunk_size);
+ SaveScore_SCOR(file, &scores);
+ }
putFileChunkBE(file, "TIME", time_chunk_size);
SaveScore_TIME(file, &scores);
SaveScoreToFilename(filename);
}
-void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data,
- char *error)
-{
-#if defined(PLATFORM_EMSCRIPTEN)
- // threads currently not fully supported by Emscripten/SDL and some browsers
- function(data);
-#else
- SDL_Thread *thread = SDL_CreateThread(function, name, data);
-
- if (thread != NULL)
- SDL_DetachThread(thread);
- else
- Error("Cannot create thread to %s!", error);
-
- // nasty kludge to lower probability of intermingled thread error messages
- Delay(1);
-#endif
-}
-
-char *getPasswordJSON(char *password)
-{
- static char password_json[MAX_FILENAME_LEN] = "";
- static boolean initialized = FALSE;
-
- if (!initialized)
- {
- if (password != NULL &&
- !strEqual(password, "") &&
- !strEqual(password, UNDEFINED_PASSWORD))
- snprintf(password_json, MAX_FILENAME_LEN,
- " \"password\": \"%s\",\n",
- setup.api_server_password);
-
- initialized = TRUE;
- }
-
- return password_json;
-}
-
-struct ApiGetScoreThreadData
-{
- int level_nr;
- char *score_cache_filename;
-};
-
-static void *CreateThreadData_ApiGetScore(int nr)
-{
- struct ApiGetScoreThreadData *data =
- checked_malloc(sizeof(struct ApiGetScoreThreadData));
- char *score_cache_filename = getScoreCacheFilename(nr);
-
- data->level_nr = nr;
- data->score_cache_filename = getStringCopy(score_cache_filename);
-
- return data;
-}
-
-static void FreeThreadData_ApiGetScore(void *data_raw)
-{
- struct ApiGetScoreThreadData *data = data_raw;
-
- checked_free(data->score_cache_filename);
- checked_free(data);
-}
-
-static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
- void *data_raw)
-{
- struct ApiGetScoreThreadData *data = data_raw;
- int level_nr = data->level_nr;
-
- request->hostname = setup.api_server_hostname;
- request->port = API_SERVER_PORT;
- request->method = API_SERVER_METHOD;
- request->uri = API_SERVER_URI_GET;
-
- snprintf(request->body, MAX_HTTP_BODY_SIZE,
- "{\n"
- "%s"
- " \"game_version\": \"%s\",\n"
- " \"game_platform\": \"%s\",\n"
- " \"levelset_identifier\": \"%s\",\n"
- " \"level_nr\": \"%d\"\n"
- "}\n",
- getPasswordJSON(setup.api_server_password),
- getProgramRealVersionString(),
- getProgramPlatformString(),
- levelset.identifier,
- level_nr);
-
- ConvertHttpRequestBodyToServerEncoding(request);
-
- return TRUE;
-}
-
-static void HandleResponse_ApiGetScore(struct HttpResponse *response,
- void *data_raw)
-{
- struct ApiGetScoreThreadData *data = data_raw;
-
- if (response->body_size == 0)
- {
- // no scores available for this level
-
- return;
- }
-
- ConvertHttpResponseBodyToClientEncoding(response);
-
- char *filename = data->score_cache_filename;
- FILE *file;
- int i;
-
- // used instead of "leveldir_current->subdir" (for network games)
- InitScoreCacheDirectory(levelset.identifier);
-
- if (!(file = fopen(filename, MODE_WRITE)))
- {
- Warn("cannot save score cache 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.updated = TRUE;
-}
-
-#if defined(PLATFORM_EMSCRIPTEN)
-static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw,
- void *buffer, unsigned int size)
-{
- struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
-
- if (response != NULL)
- {
- HandleResponse_ApiGetScore(response, data_raw);
-
- checked_free(response);
- }
- else
- {
- Error("server response too large to handle (%d bytes)", size);
- }
-
- FreeThreadData_ApiGetScore(data_raw);
-}
-
-static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw,
- int code, const char *status)
-{
- Error("server failed to handle request: %d %s", code, status);
-
- FreeThreadData_ApiGetScore(data_raw);
-}
-
-static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw,
- int bytes, int size)
-{
- // nothing to do here
-}
-
-static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request,
- void *data_raw)
-{
- if (!SetRequest_ApiGetScore(request, data_raw))
- {
- FreeThreadData_ApiGetScore(data_raw);
-
- return;
- }
-
- emscripten_async_wget2_data(request->uri,
- request->method,
- request->body,
- data_raw,
- TRUE,
- Emscripten_ApiGetScore_Loaded,
- Emscripten_ApiGetScore_Failed,
- Emscripten_ApiGetScore_Progress);
-}
-
-#else
-
-static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
- struct HttpResponse *response,
- void *data_raw)
-{
- if (!SetRequest_ApiGetScore(request, data_raw))
- return;
-
- if (!DoHttpRequest(request, response))
- {
- Error("HTTP request failed: %s", GetHttpError());
-
- return;
- }
-
- if (!HTTP_SUCCESS(response->status_code))
- {
- Error("server failed to handle request: %d %s",
- response->status_code,
- response->status_text);
-
- return;
- }
-
- HandleResponse_ApiGetScore(response, data_raw);
-}
-
-static void ApiGetScore_HttpRequest(struct HttpRequest *request,
- struct HttpResponse *response,
- void *data_raw)
-{
- ApiGetScore_HttpRequestExt(request, response, data_raw);
-
- FreeThreadData_ApiGetScore(data_raw);
-}
-#endif
-
-static int ApiGetScoreThread(void *data_raw)
-{
- struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
- struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
-
-#if defined(PLATFORM_EMSCRIPTEN)
- Emscripten_ApiGetScore_HttpRequest(request, data_raw);
-#else
- ApiGetScore_HttpRequest(request, response, data_raw);
-#endif
-
- checked_free(request);
- checked_free(response);
-
- return 0;
-}
-
-static void ApiGetScoreAsThread(int nr)
-{
- struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr);
-
- ExecuteAsThread(ApiGetScoreThread,
- "ApiGetScore", data,
- "download scores from server");
-}
-
static void LoadServerScoreFromCache(int nr)
{
struct ScoreEntry score_entry;
{ &score_entry.time, FALSE, 0 },
{ score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
{ score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
+ { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
+ { &score_entry.id, FALSE, 0 },
+ { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
+ { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
+ { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
+ { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
{ NULL, FALSE, 0 }
};
}
}
-static char *get_file_base64(char *filename)
-{
- struct stat file_status;
-
- if (stat(filename, &file_status) != 0)
- {
- Error("cannot stat file '%s'", filename);
-
- return NULL;
- }
-
- int buffer_size = file_status.st_size;
- byte *buffer = checked_malloc(buffer_size);
- FILE *file;
- int i;
-
- if (!(file = fopen(filename, MODE_READ)))
- {
- Error("cannot open file '%s'", filename);
-
- checked_free(buffer);
-
- return NULL;
- }
-
- for (i = 0; i < buffer_size; i++)
- {
- int c = fgetc(file);
-
- if (c == EOF)
- {
- Error("cannot read from input file '%s'", filename);
-
- fclose(file);
- checked_free(buffer);
-
- return NULL;
- }
-
- buffer[i] = (byte)c;
- }
-
- fclose(file);
-
- int buffer_encoded_size = base64_encoded_size(buffer_size);
- char *buffer_encoded = checked_malloc(buffer_encoded_size);
-
- base64_encode(buffer_encoded, buffer, buffer_size);
-
- checked_free(buffer);
-
- return buffer_encoded;
-}
-
-struct ApiAddScoreThreadData
-{
- int level_nr;
- char *score_tape_filename;
- struct ScoreEntry score_entry;
-};
-
-static void *CreateThreadData_ApiAddScore(int nr, char *score_tape_filename)
-{
- struct ApiAddScoreThreadData *data =
- checked_malloc(sizeof(struct ApiAddScoreThreadData));
- struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
-
- if (score_tape_filename == NULL)
- score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
-
- data->level_nr = nr;
- data->score_entry = *score_entry;
- data->score_tape_filename = getStringCopy(score_tape_filename);
-
- return data;
-}
-
-static void FreeThreadData_ApiAddScore(void *data_raw)
-{
- struct ApiAddScoreThreadData *data = data_raw;
-
- checked_free(data->score_tape_filename);
- checked_free(data);
-}
-
-static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
- void *data_raw)
-{
- struct ApiAddScoreThreadData *data = data_raw;
- struct ScoreEntry *score_entry = &data->score_entry;
- char *score_tape_filename = data->score_tape_filename;
- int level_nr = data->level_nr;
-
- request->hostname = setup.api_server_hostname;
- request->port = API_SERVER_PORT;
- request->method = API_SERVER_METHOD;
- request->uri = API_SERVER_URI_ADD;
-
- char *tape_base64 = get_file_base64(score_tape_filename);
-
- if (tape_base64 == NULL)
- {
- Error("loading and base64 encoding score tape file failed");
-
- return FALSE;
- }
-
- char *player_name_raw = score_entry->name;
- char *player_uuid_raw = setup.player_uuid;
-
- if (options.player_name != NULL && global.autoplay_leveldir != NULL)
- {
- player_name_raw = options.player_name;
- player_uuid_raw = "";
- }
-
- char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
- char *levelset_name = getEscapedJSON(leveldir_current->name);
- char *levelset_author = getEscapedJSON(leveldir_current->author);
- char *level_name = getEscapedJSON(level.name);
- char *level_author = getEscapedJSON(level.author);
- char *player_name = getEscapedJSON(player_name_raw);
- char *player_uuid = getEscapedJSON(player_uuid_raw);
-
- snprintf(request->body, MAX_HTTP_BODY_SIZE,
- "{\n"
- "%s"
- " \"game_version\": \"%s\",\n"
- " \"game_platform\": \"%s\",\n"
- " \"batch_time\": \"%d\",\n"
- " \"levelset_identifier\": \"%s\",\n"
- " \"levelset_name\": \"%s\",\n"
- " \"levelset_author\": \"%s\",\n"
- " \"levelset_num_levels\": \"%d\",\n"
- " \"levelset_first_level\": \"%d\",\n"
- " \"level_nr\": \"%d\",\n"
- " \"level_name\": \"%s\",\n"
- " \"level_author\": \"%s\",\n"
- " \"rate_time_over_score\": \"%d\",\n"
- " \"player_name\": \"%s\",\n"
- " \"player_uuid\": \"%s\",\n"
- " \"score\": \"%d\",\n"
- " \"time\": \"%d\",\n"
- " \"tape_basename\": \"%s\",\n"
- " \"tape\": \"%s\"\n"
- "}\n",
- getPasswordJSON(setup.api_server_password),
- getProgramRealVersionString(),
- getProgramPlatformString(),
- (int)global.autoplay_time,
- levelset_identifier,
- levelset_name,
- levelset_author,
- leveldir_current->levels,
- leveldir_current->first_level,
- level_nr,
- level_name,
- level_author,
- level.rate_time_over_score,
- player_name,
- player_uuid,
- score_entry->score,
- score_entry->time,
- score_entry->tape_basename,
- tape_base64);
-
- checked_free(tape_base64);
-
- checked_free(levelset_identifier);
- checked_free(levelset_name);
- checked_free(levelset_author);
- checked_free(level_name);
- checked_free(level_author);
- checked_free(player_name);
- checked_free(player_uuid);
-
- ConvertHttpRequestBodyToServerEncoding(request);
-
- return TRUE;
-}
-
-static void HandleResponse_ApiAddScore(struct HttpResponse *response,
- void *data_raw)
-{
- server_scores.uploaded = TRUE;
-}
-
-#if defined(PLATFORM_EMSCRIPTEN)
-static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
- void *buffer, unsigned int size)
-{
- struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
-
- if (response != NULL)
- {
- HandleResponse_ApiAddScore(response, data_raw);
-
- checked_free(response);
- }
- else
- {
- Error("server response too large to handle (%d bytes)", size);
- }
-
- FreeThreadData_ApiAddScore(data_raw);
-}
-
-static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
- int code, const char *status)
+void PrepareScoreTapesForUpload(char *leveldir_subdir)
{
- Error("server failed to handle request: %d %s", code, status);
-
- FreeThreadData_ApiAddScore(data_raw);
-}
+ MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
-static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw,
- int bytes, int size)
-{
- // nothing to do here
-}
-
-static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request,
- void *data_raw)
-{
- if (!SetRequest_ApiAddScore(request, data_raw))
- {
- FreeThreadData_ApiAddScore(data_raw);
+ // if score tape not uploaded, ask for uploading missing tapes later
+ if (!setup.has_remaining_tapes)
+ setup.ask_for_remaining_tapes = TRUE;
- return;
- }
+ setup.provide_uploading_tapes = TRUE;
+ setup.has_remaining_tapes = TRUE;
- emscripten_async_wget2_data(request->uri,
- request->method,
- request->body,
- data_raw,
- TRUE,
- Emscripten_ApiAddScore_Loaded,
- Emscripten_ApiAddScore_Failed,
- Emscripten_ApiAddScore_Progress);
+ SaveSetup_ServerSetup();
}
-#else
-
-static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
- struct HttpResponse *response,
- void *data_raw)
+void SaveServerScore(int nr, boolean tape_saved)
{
- if (!SetRequest_ApiAddScore(request, data_raw))
- return;
-
- if (!DoHttpRequest(request, response))
- {
- Error("HTTP request failed: %s", GetHttpError());
-
- return;
- }
-
- if (!HTTP_SUCCESS(response->status_code))
+ if (!runtime.use_api_server)
{
- Error("server failed to handle request: %d %s",
- response->status_code,
- response->status_text);
+ PrepareScoreTapesForUpload(leveldir_current->subdir);
return;
}
- HandleResponse_ApiAddScore(response, data_raw);
-}
-
-static void ApiAddScore_HttpRequest(struct HttpRequest *request,
- struct HttpResponse *response,
- void *data_raw)
-{
- ApiAddScore_HttpRequestExt(request, response, data_raw);
-
- FreeThreadData_ApiAddScore(data_raw);
-}
-#endif
-
-static int ApiAddScoreThread(void *data_raw)
-{
- struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
- struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
-
-#if defined(PLATFORM_EMSCRIPTEN)
- Emscripten_ApiAddScore_HttpRequest(request, data_raw);
-#else
- ApiAddScore_HttpRequest(request, response, data_raw);
-#endif
-
- checked_free(request);
- checked_free(response);
-
- return 0;
-}
-
-static void ApiAddScoreAsThread(int nr, char *score_tape_filename)
-{
- struct ApiAddScoreThreadData *data =
- CreateThreadData_ApiAddScore(nr, score_tape_filename);
-
- ExecuteAsThread(ApiAddScoreThread,
- "ApiAddScore", data,
- "upload score to server");
+ ApiAddScoreAsThread(nr, tape_saved, NULL);
}
-void SaveServerScore(int nr)
+void SaveServerScoreFromFile(int nr, boolean tape_saved,
+ char *score_tape_filename)
{
if (!runtime.use_api_server)
return;
- ApiAddScoreAsThread(nr, NULL);
-}
-
-void SaveServerScoreFromFile(int nr, char *score_tape_filename)
-{
- if (!runtime.use_api_server)
- return;
-
- ApiAddScoreAsThread(nr, score_tape_filename);
+ ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
}
void LoadLocalAndServerScore(int nr, boolean download_score)
{
int last_added_local = scores.last_added_local;
+ boolean force_last_added = scores.force_last_added;
+
+ // needed if only showing server scores
+ setScoreInfoToDefaults();
- LoadScore(nr);
+ if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
+ LoadScore(nr);
// restore last added local score entry (before merging server scores)
scores.last_added = scores.last_added_local = last_added_local;
- if (setup.use_api_server && !setup.only_show_local_scores)
+ if (setup.use_api_server &&
+ !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
{
// load server scores from cache file and trigger update from server
LoadServerScore(nr, download_score);
// merge local scores with scores from server
MergeServerScore();
}
+
+ if (force_last_added)
+ scores.force_last_added = force_last_added;
}
TYPE_SWITCH,
&setup.autorecord, "automatic_tape_recording"
},
+ {
+ TYPE_SWITCH,
+ &setup.auto_pause_on_start, "auto_pause_on_start"
+ },
{
TYPE_SWITCH,
&setup.show_titlescreen, "show_titlescreen"
&setup.show_undo_redo_buttons, "show_undo_redo_buttons"
},
{
- TYPE_SWITCH,
- &setup.only_show_local_scores, "only_show_local_scores"
+ TYPE_STRING,
+ &setup.scores_in_highscore_list, "scores_in_highscore_list"
},
{
TYPE_STRING,
TYPE_INTEGER,
&setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
},
+ {
+ TYPE_SWITCH,
+ &setup.touch.overlay_buttons, "touch.overlay_buttons"
+ },
};
static struct TokenInfo auto_setup_tokens[] =
TYPE_STRING,
&setup.player_uuid, "player_uuid"
},
+ {
+ TYPE_INTEGER,
+ &setup.player_version, "player_version"
+ },
{
TYPE_SWITCH,
&setup.use_api_server, TEST_PREFIX "use_api_server"
TYPE_SWITCH,
&setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
},
+ {
+ TYPE_SWITCH,
+ &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
+ },
{
TYPE_SWITCH,
&setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
},
+ {
+ TYPE_SWITCH,
+ &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
+ },
};
static struct TokenInfo editor_setup_tokens[] =
TYPE_SWITCH,
&setup.editor_cascade.el_ge, "editor.cascade.el_ge"
},
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_es, "editor.cascade.el_es"
+ },
{
TYPE_SWITCH,
&setup.editor_cascade.el_ref, "editor.cascade.el_ref"
TYPE_KEY_X11,
&setup.shortcut.load_game, "shortcut.load_game"
},
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.restart_game, "shortcut.restart_game"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
+ },
{
TYPE_KEY_X11,
&setup.shortcut.toggle_pause, "shortcut.toggle_pause"
TYPE_BOOLEAN,
&setup.internal.menu_save_and_exit, "menu_save_and_exit"
},
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_title, "info_title"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_elements, "info_elements"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_music, "info_music"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_credits, "info_credits"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_program, "info_program"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_version, "info_version"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_levelset, "info_levelset"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_exit, "info_exit"
+ },
};
static struct TokenInfo debug_setup_tokens[] =
si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
si->fade_screens = TRUE;
si->autorecord = TRUE;
+ si->auto_pause_on_start = FALSE;
si->show_titlescreen = TRUE;
si->quick_doors = FALSE;
si->team_mode = FALSE;
si->small_game_graphics = FALSE;
si->show_load_save_buttons = FALSE;
si->show_undo_redo_buttons = FALSE;
- si->only_show_local_scores = FALSE;
+ si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
si->touch.grid_initialized = video.initialized;
+ si->touch.overlay_buttons = FALSE;
+
si->editor.el_boulderdash = TRUE;
si->editor.el_emerald_mine = TRUE;
si->editor.el_emerald_mine_club = TRUE;
si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
+ si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
+ si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
#if defined(PLATFORM_ANDROID)
si->fullscreen = TRUE;
+ si->touch.overlay_buttons = TRUE;
#endif
setHideSetupEntry(&setup.debug.xsn_mode);
static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
{
si->player_uuid = NULL; // (will be set later)
+ si->player_version = 1; // (will be set later)
si->use_api_server = TRUE;
si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
si->ask_for_uploading_tapes = TRUE;
+ si->ask_for_remaining_tapes = FALSE;
si->provide_uploading_tapes = TRUE;
+ si->ask_for_using_api_server = TRUE;
+ si->has_remaining_tapes = FALSE;
}
static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
si->editor_cascade.el_steel_chars = FALSE;
si->editor_cascade.el_ce = FALSE;
si->editor_cascade.el_ge = FALSE;
+ si->editor_cascade.el_es = FALSE;
si->editor_cascade.el_ref = FALSE;
si->editor_cascade.el_user = FALSE;
si->editor_cascade.el_dynamic = FALSE;
// try to load setup values from default setup file
filename = getDefaultSetupFilename();
+ if (fileExists(filename))
+ LoadSetupFromFilename(filename);
+
+ // try to load setup values from platform setup file
+ filename = getPlatformSetupFilename();
+
if (fileExists(filename))
LoadSetupFromFilename(filename);
{
// player UUID does not yet exist in setup file
setup.player_uuid = getStringCopy(getUUID());
+ setup.player_version = 2;
SaveSetup_ServerSetup();
}
result = -(int)key;
}
+ if (result == -1)
+ {
+ if (isURL(token))
+ {
+ result = get_hash_from_key(token); // unsigned int => int
+ result = ABS(result); // may be negative now
+ result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
+
+ setHashEntry(anim_url_hash, int2str(result, 0), token);
+ }
+ }
+
if (result == -1)
result = ANIM_EVENT_ACTION_NONE;
string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
string_has_parameter(value, "random") ? ANIM_RANDOM :
+ string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
string_has_parameter(value, "centered") ? ANIM_CENTERED :
string_has_parameter(value, "all") ? ANIM_ALL :
+ string_has_parameter(value, "tiled") ? ANIM_TILED :
ANIM_DEFAULT);
if (string_has_parameter(value, "once"))
return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
}
-void InitMenuDesignSettings_Static(void)
+void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
+ boolean ignore_defaults)
{
int i;
- // always start with reliable default values from static default config
for (i = 0; image_config_vars[i].token != NULL; i++)
{
- char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
+ char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
+
+ // (ignore definitions set to "[DEFAULT]" which are already initialized)
+ if (ignore_defaults && strEqual(value, ARG_DEFAULT))
+ continue;
if (value != NULL)
*image_config_vars[i].value =
}
}
+void InitMenuDesignSettings_Static(void)
+{
+ // always start with reliable default values from static default config
+ InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
+}
+
static void InitMenuDesignSettings_SpecialPreProcessing(void)
{
int i;
*menu_config_players[i].value = TRUE;
// read (and overwrite with) values that may be specified in config file
- for (i = 0; image_config_vars[i].token != NULL; i++)
- {
- char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
-
- // (ignore definitions set to "[DEFAULT]" which are already initialized)
- if (value != NULL && !strEqual(value, ARG_DEFAULT))
- *image_config_vars[i].value =
- get_token_parameter_value(image_config_vars[i].token, value);
- }
+ InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
freeSetupFileHash(setup_file_hash);
}
new = &(*new)->next;
}
}
+
+ // add pointers to previous list nodes
+
+ struct MusicFileInfo *node = music_file_info;
+
+ while (node != NULL)
+ {
+ if (node->next)
+ node->next->prev = node;
+
+ node = node->next;
+ }
}
static void add_helpanim_entry(int element, int action, int direction,