#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
{
// 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)
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
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
+ // - continue_playing
}
static void setScoreInfoToDefaults(void)
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;
-
- char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
- char *levelset_name = getEscapedJSON(leveldir_current->name);
-
- snprintf(request->body, MAX_HTTP_BODY_SIZE,
- "{\n"
- "%s"
- " \"game_version\": \"%s\",\n"
- " \"game_platform\": \"%s\",\n"
- " \"levelset_identifier\": \"%s\",\n"
- " \"levelset_name\": \"%s\",\n"
- " \"level_nr\": \"%d\"\n"
- "}\n",
- getPasswordJSON(setup.api_server_password),
- getProgramRealVersionString(),
- getProgramPlatformString(),
- levelset_identifier,
- levelset_name,
- level_nr);
-
- checked_free(levelset_identifier);
- checked_free(levelset_name);
-
- 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))
- {
- // 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_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));
-
- program.api_thread_count++;
-
-#if defined(PLATFORM_EMSCRIPTEN)
- Emscripten_ApiGetScore_HttpRequest(request, data_raw);
-#else
- ApiGetScore_HttpRequest(request, response, data_raw);
-#endif
-
- program.api_thread_count--;
-
- 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;
}
}
-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;
-}
-
-static void PrepareScoreTapesForUpload(char *leveldir_subdir)
+void PrepareScoreTapesForUpload(char *leveldir_subdir)
{
MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
SaveSetup_ServerSetup();
}
-struct ApiAddScoreThreadData
-{
- int level_nr;
- boolean tape_saved;
- char *leveldir_subdir;
- char *score_tape_filename;
- struct ScoreEntry score_entry;
-};
-
-static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
- 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->tape_saved = tape_saved;
- data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
- data->score_tape_filename = getStringCopy(score_tape_filename);
- data->score_entry = *score_entry;
-
- return data;
-}
-
-static void FreeThreadData_ApiAddScore(void *data_raw)
-{
- struct ApiAddScoreThreadData *data = data_raw;
-
- checked_free(data->leveldir_subdir);
- 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;
- boolean tape_saved = data->tape_saved;
- 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"
- " \"use_step_counter\": \"%d\",\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_saved\": \"%d\",\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.use_step_counter,
- level.rate_time_over_score,
- player_name,
- player_uuid,
- score_entry->score,
- score_entry->time,
- score_entry->tape_basename,
- tape_saved,
- 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;
-}
-
-static void HandleFailure_ApiAddScore(void *data_raw)
-{
- struct ApiAddScoreThreadData *data = data_raw;
-
- PrepareScoreTapesForUpload(data->leveldir_subdir);
-}
-
-#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);
-
- HandleFailure_ApiAddScore(data_raw);
- }
-
- FreeThreadData_ApiAddScore(data_raw);
-}
-
-static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
- int code, const char *status)
-{
- Error("server failed to handle request: %d %s", code, status);
-
- HandleFailure_ApiAddScore(data_raw);
-
- FreeThreadData_ApiAddScore(data_raw);
-}
-
-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);
-
- return;
- }
-
- emscripten_async_wget2_data(request->uri,
- request->method,
- request->body,
- data_raw,
- TRUE,
- Emscripten_ApiAddScore_Loaded,
- Emscripten_ApiAddScore_Failed,
- Emscripten_ApiAddScore_Progress);
-}
-
-#else
-
-static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
- struct HttpResponse *response,
- void *data_raw)
-{
- if (!SetRequest_ApiAddScore(request, data_raw))
- return;
-
- if (!DoHttpRequest(request, response))
- {
- Error("HTTP request failed: %s", GetHttpError());
-
- HandleFailure_ApiAddScore(data_raw);
-
- return;
- }
-
- if (!HTTP_SUCCESS(response->status_code))
- {
- Error("server failed to handle request: %d %s",
- response->status_code,
- response->status_text);
-
- HandleFailure_ApiAddScore(data_raw);
-
- 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));
-
- program.api_thread_count++;
-
-#if defined(PLATFORM_EMSCRIPTEN)
- Emscripten_ApiAddScore_HttpRequest(request, data_raw);
-#else
- ApiAddScore_HttpRequest(request, response, data_raw);
-#endif
-
- program.api_thread_count--;
-
- checked_free(request);
- checked_free(response);
-
- return 0;
-}
-
-static void ApiAddScoreAsThread(int nr, boolean tape_saved,
- char *score_tape_filename)
-{
- struct ApiAddScoreThreadData *data =
- CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
-
- ExecuteAsThread(ApiAddScoreThread,
- "ApiAddScore", data,
- "upload score to server");
-}
-
void SaveServerScore(int nr, boolean tape_saved)
{
if (!runtime.use_api_server)
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();
// merge local scores with scores from server
MergeServerScore();
}
+
+ if (force_last_added)
+ scores.force_last_added = force_last_added;
}
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[] =
// 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);
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;
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,