X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=83cbc05947dbb181f793d4b7c2610d3154541b61;hb=e4b2e4f943c20af98956d30fd3014888e9f75416;hp=104ecb4d3560b2f29fa9ac41cfd59569036cf557;hpb=71dca6319ace9a34fdc0e406800c22018ac2e285;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index 104ecb4d..83cbc059 100644 --- a/src/files.c +++ b/src/files.c @@ -6610,6 +6610,27 @@ static void LoadLevel_InitCustomElements(struct LevelInfo *level) element_info[element].ignition_delay = 8; } } + + // set mouse click change events to work for left/middle/right mouse button + if (level->game_version < VERSION_IDENT(4,2,3,0)) + { + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + struct ElementInfo *ei = &element_info[element]; + + for (j = 0; j < ei->num_change_pages; j++) + { + struct ElementChangeInfo *change = &ei->change_page[j]; + + if (change->has_event[CE_CLICKED_BY_MOUSE] || + change->has_event[CE_PRESSED_BY_MOUSE] || + change->has_event[CE_MOUSE_CLICKED_ON_X] || + change->has_event[CE_MOUSE_PRESSED_ON_X]) + change->trigger_side = CH_SIDE_ANY; + } + } + } } static void LoadLevel_InitElements(struct LevelInfo *level) @@ -8274,13 +8295,10 @@ void SaveTapeToFilename(char *filename) SetFilePermissions(filename, PERMS_PRIVATE); } -void SaveTape(int nr) +static void SaveTapeExt(char *filename) { - char *filename = getTapeFilename(nr); int i; - InitTapeDirectory(leveldir_current->subdir); - tape.file_version = FILE_VERSION_ACTUAL; tape.game_version = GAME_VERSION_ACTUAL; @@ -8296,6 +8314,25 @@ void SaveTape(int nr) tape.changed = FALSE; } +void SaveTape(int nr) +{ + char *filename = getTapeFilename(nr); + + InitTapeDirectory(leveldir_current->subdir); + + SaveTapeExt(filename); +} + +void SaveScoreTape(int nr) +{ + char *filename = getScoreTapeFilename(tape.score_tape_basename, nr); + + // used instead of "leveldir_current->subdir" (for network games) + InitScoreTapeDirectory(levelset.identifier, nr); + + SaveTapeExt(filename); +} + static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved, unsigned int req_state_added) { @@ -8387,16 +8424,30 @@ void DumpTape(struct TapeInfo *tape) // score file functions // ============================================================================ -static void setScoreInfoToDefaults(void) +static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores) { int i; for (i = 0; i < MAX_SCORE_ENTRIES; i++) { - strcpy(scores.entry[i].name, EMPTY_PLAYER_NAME); - scores.entry[i].score = 0; - scores.entry[i].time = 0; + strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME); + strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME); + scores->entry[i].score = 0; + scores->entry[i].time = 0; } + + scores->num_entries = 0; + scores->last_added = -1; +} + +static void setScoreInfoToDefaults(void) +{ + setScoreInfoToDefaultsExt(&scores); +} + +static void setServerScoreInfoToDefaults(void) +{ + setScoreInfoToDefaultsExt(&server_scores); } static void LoadScore_OLD(int nr) @@ -8451,6 +8502,25 @@ static void LoadScore_OLD(int nr) fclose(file); } +static void ConvertScore_OLD(void) +{ + // only convert score to time for levels that rate playing time over score + if (!level.rate_time_over_score) + return; + + // convert old score to playing time for score-less levels (like Supaplex) + int time_final_max = 999; + int i; + + for (i = 0; i < MAX_SCORE_ENTRIES; i++) + { + int score = scores.entry[i].score; + + if (score > 0 && score < time_final_max) + scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND; + } +} + static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores) { scores->file_version = getFileVersion(file); @@ -8526,6 +8596,23 @@ static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores) return chunk_size; } +static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores) +{ + int i, j; + + for (i = 0; i < scores->num_entries; i++) + { + for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++) + scores->entry[i].tape_basename[j] = getFile8Bit(file); + + scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0'; + } + + chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN; + + return chunk_size; +} + void LoadScore(int nr) { char *filename = getScoreFilename(nr); @@ -8580,6 +8667,9 @@ void LoadScore(int nr) { // score files from versions before 4.2.4.0 without chunk structure LoadScore_OLD(nr); + + // convert score to time, if possible (mainly for Supaplex levels) + ConvertScore_OLD(); } else { @@ -8596,6 +8686,7 @@ void LoadScore(int nr) { "NAME", -1, LoadScore_NAME }, { "SCOR", -1, LoadScore_SCOR }, { "TIME", -1, LoadScore_TIME }, + { "TAPE", -1, LoadScore_TAPE }, { NULL, 0, NULL } }; @@ -8722,6 +8813,19 @@ static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores) putFile32BitBE(file, scores->entry[i].time); } +static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores) +{ + int i, j; + + for (i = 0; i < scores->num_entries; i++) + { + int size = strlen(scores->entry[i].tape_basename); + + for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++) + putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0)); + } +} + static void SaveScoreToFilename(char *filename) { FILE *file; @@ -8730,6 +8834,7 @@ static void SaveScoreToFilename(char *filename) int name_chunk_size; int scor_chunk_size; int time_chunk_size; + int tape_chunk_size; if (!(file = fopen(filename, MODE_WRITE))) { @@ -8742,6 +8847,7 @@ static void SaveScoreToFilename(char *filename) name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN; scor_chunk_size = scores.num_entries * 2; time_chunk_size = scores.num_entries * 4; + tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN; putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED); putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE); @@ -8761,6 +8867,9 @@ static void SaveScoreToFilename(char *filename) putFileChunkBE(file, "TIME", time_chunk_size); SaveScore_TIME(file, &scores); + putFileChunkBE(file, "TAPE", tape_chunk_size); + SaveScore_TAPE(file, &scores); + fclose(file); SetFilePermissions(filename, permissions); @@ -8795,6 +8904,296 @@ void SaveScore(int nr) SaveScoreToFilename(filename); } +static void DownloadServerScoreToCacheExt(struct HttpRequest *request, + struct HttpResponse *response, + int nr) +{ + request->hostname = 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" + " \"levelset_identifier\": \"%s\",\n" + " \"level_nr\": \"%d\",\n" + " \"rate_time_over_score\": \"%d\"\n" + "}\n", + levelset.identifier, nr, level.rate_time_over_score); + + 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; + } + + if (response->body_size == 0) + { + // no scores available for this level + + return; + } + + ConvertHttpResponseBodyToClientEncoding(response); + + char *filename = getScoreCacheFilename(nr); + 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); +} + +static void DownloadServerScoreToCache(int nr) +{ + struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); + struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); + + DownloadServerScoreToCacheExt(request, response, nr); + + checked_free(request); + checked_free(response); +} + +static void LoadServerScoreFromCache(int nr) +{ + struct ScoreEntry score_entry; + struct + { + void *value; + boolean is_string; + int string_size; + } + score_mapping[] = + { + { &score_entry.score, FALSE, 0 }, + { &score_entry.time, FALSE, 0 }, + { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN }, + { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN }, + + { NULL, FALSE, 0 } + }; + char *filename = getScoreCacheFilename(nr); + SetupFileHash *score_hash = loadSetupFileHash(filename); + int i, j; + + server_scores.num_entries = 0; + + if (score_hash == NULL) + return; + + for (i = 0; i < MAX_SCORE_ENTRIES; i++) + { + score_entry = server_scores.entry[i]; + + for (j = 0; score_mapping[j].value != NULL; j++) + { + char token[10]; + + sprintf(token, "%02d.%d", i, j); + + char *value = getHashEntry(score_hash, token); + + if (value == NULL) + continue; + + if (score_mapping[j].is_string) + { + char *score_value = (char *)score_mapping[j].value; + int value_size = score_mapping[j].string_size; + + strncpy(score_value, value, value_size); + score_value[value_size] = '\0'; + } + else + { + int *score_value = (int *)score_mapping[j].value; + + *score_value = atoi(value); + } + + server_scores.num_entries = i + 1; + } + + server_scores.entry[i] = score_entry; + } + + freeSetupFileHash(score_hash); +} + +void LoadServerScore(int nr) +{ + // always start with reliable default values + setServerScoreInfoToDefaults(); + + DownloadServerScoreToCache(nr); + LoadServerScoreFromCache(nr); + + MergeServerScore(); +} + +static char *get_file_base64(char *filename) +{ + struct stat file_status; + + if (stat(filename, &file_status) != 0) + { + Error("cannot stat file '%s'\n", 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'\n", 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'\n", 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 UploadScoreToServerExt(struct HttpRequest *request, + struct HttpResponse *response, + int nr) +{ + struct ScoreEntry *score_entry = &scores.entry[scores.last_added]; + + request->hostname = API_SERVER_HOSTNAME; + request->port = API_SERVER_PORT; + request->method = API_SERVER_METHOD; + request->uri = API_SERVER_URI_ADD; + + char *tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr); + char *tape_base64 = get_file_base64(tape_filename); + + if (tape_base64 == NULL) + { + Error("loading and base64 encoding score tape file failed"); + + return; + } + + snprintf(request->body, MAX_HTTP_BODY_SIZE, + "{\n" + " \"game_version\": \"%s\",\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" + " \"player_name\": \"%s\",\n" + " \"score\": \"%d\",\n" + " \"time\": \"%d\",\n" + " \"tape_basename\": \"%s\",\n" + " \"tape\": \"%s\"\n" + "}\n", + getProgramRealVersionString(), + leveldir_current->identifier, + leveldir_current->name, + leveldir_current->author, + leveldir_current->levels, + leveldir_current->first_level, + level_nr, + score_entry->name, + score_entry->score, + score_entry->time, + score_entry->tape_basename, + tape_base64); + + ConvertHttpRequestBodyToServerEncoding(request); + + 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; + } +} + +static void UploadScoreToServer(int nr) +{ + struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); + struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); + + UploadScoreToServerExt(request, response, nr); + + checked_free(request); + checked_free(response); +} + +void SaveServerScore(int nr) +{ + UploadScoreToServer(nr); +} + // ============================================================================ // setup file functions