duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

rc_api_user.c (9655B)


      1 #include "rc_api_user.h"
      2 #include "rc_api_common.h"
      3 
      4 #include "../rc_version.h"
      5 
      6 #include <string.h>
      7 
      8 /* --- Login --- */
      9 
     10 int rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_request_t* api_params) {
     11   rc_api_url_builder_t builder;
     12 
     13   rc_api_url_build_dorequest_url(request);
     14 
     15   if (!api_params->username || !*api_params->username)
     16     return RC_INVALID_STATE;
     17 
     18   rc_url_builder_init(&builder, &request->buffer, 48);
     19   rc_url_builder_append_str_param(&builder, "r", "login2");
     20   rc_url_builder_append_str_param(&builder, "u", api_params->username);
     21 
     22   if (api_params->password && api_params->password[0])
     23     rc_url_builder_append_str_param(&builder, "p", api_params->password);
     24   else if (api_params->api_token && api_params->api_token[0])
     25     rc_url_builder_append_str_param(&builder, "t", api_params->api_token);
     26   else
     27     return RC_INVALID_STATE;
     28 
     29   request->post_data = rc_url_builder_finalize(&builder);
     30   request->content_type = RC_CONTENT_TYPE_URLENCODED;
     31 
     32   return builder.result;
     33 }
     34 
     35 int rc_api_process_login_response(rc_api_login_response_t* response, const char* server_response) {
     36   rc_api_server_response_t response_obj;
     37 
     38   memset(&response_obj, 0, sizeof(response_obj));
     39   response_obj.body = server_response;
     40   response_obj.body_length = rc_json_get_object_string_length(server_response);
     41 
     42   return rc_api_process_login_server_response(response, &response_obj);
     43 }
     44 
     45 int rc_api_process_login_server_response(rc_api_login_response_t* response, const rc_api_server_response_t* server_response) {
     46   int result;
     47   rc_json_field_t fields[] = {
     48     RC_JSON_NEW_FIELD("Success"),
     49     RC_JSON_NEW_FIELD("Error"),
     50     RC_JSON_NEW_FIELD("Code"),
     51     RC_JSON_NEW_FIELD("User"),
     52     RC_JSON_NEW_FIELD("Token"),
     53     RC_JSON_NEW_FIELD("Score"),
     54     RC_JSON_NEW_FIELD("SoftcoreScore"),
     55     RC_JSON_NEW_FIELD("Messages"),
     56     RC_JSON_NEW_FIELD("DisplayName")
     57   };
     58 
     59   memset(response, 0, sizeof(*response));
     60   rc_buffer_init(&response->response.buffer);
     61 
     62   result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
     63   if (result != RC_OK || !response->response.succeeded)
     64     return result;
     65 
     66   if (!rc_json_get_required_string(&response->username, &response->response, &fields[3], "User"))
     67     return RC_MISSING_VALUE;
     68   if (!rc_json_get_required_string(&response->api_token, &response->response, &fields[4], "Token"))
     69     return RC_MISSING_VALUE;
     70 
     71   rc_json_get_optional_unum(&response->score, &fields[5], "Score", 0);
     72   rc_json_get_optional_unum(&response->score_softcore, &fields[6], "SoftcoreScore", 0);
     73   rc_json_get_optional_unum(&response->num_unread_messages, &fields[7], "Messages", 0);
     74 
     75   rc_json_get_optional_string(&response->display_name, &response->response, &fields[8], "DisplayName", response->username);
     76 
     77   return RC_OK;
     78 }
     79 
     80 void rc_api_destroy_login_response(rc_api_login_response_t* response) {
     81   rc_buffer_destroy(&response->response.buffer);
     82 }
     83 
     84 /* --- Start Session --- */
     85 
     86 int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_start_session_request_t* api_params) {
     87   rc_api_url_builder_t builder;
     88 
     89   rc_api_url_build_dorequest_url(request);
     90 
     91   if (api_params->game_id == 0)
     92     return RC_INVALID_STATE;
     93 
     94   rc_url_builder_init(&builder, &request->buffer, 48);
     95   if (rc_api_url_build_dorequest(&builder, "startsession", api_params->username, api_params->api_token)) {
     96     rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
     97 
     98     if (api_params->game_hash && *api_params->game_hash) {
     99       rc_url_builder_append_unum_param(&builder, "h", api_params->hardcore);
    100       rc_url_builder_append_str_param(&builder, "m", api_params->game_hash);
    101     }
    102 
    103     rc_url_builder_append_str_param(&builder, "l", RCHEEVOS_VERSION_STRING);
    104 
    105     request->post_data = rc_url_builder_finalize(&builder);
    106     request->content_type = RC_CONTENT_TYPE_URLENCODED;
    107   }
    108 
    109   return builder.result;
    110 }
    111 
    112 int rc_api_process_start_session_response(rc_api_start_session_response_t* response, const char* server_response) {
    113   rc_api_server_response_t response_obj;
    114 
    115   memset(&response_obj, 0, sizeof(response_obj));
    116   response_obj.body = server_response;
    117   response_obj.body_length = rc_json_get_object_string_length(server_response);
    118 
    119   return rc_api_process_start_session_server_response(response, &response_obj);
    120 }
    121 
    122 int rc_api_process_start_session_server_response(rc_api_start_session_response_t* response, const rc_api_server_response_t* server_response) {
    123   rc_api_unlock_entry_t* unlock;
    124   rc_json_field_t array_field;
    125   rc_json_iterator_t iterator;
    126   uint32_t timet;
    127   int result;
    128 
    129   rc_json_field_t fields[] = {
    130     RC_JSON_NEW_FIELD("Success"),
    131     RC_JSON_NEW_FIELD("Error"),
    132     RC_JSON_NEW_FIELD("Unlocks"),
    133     RC_JSON_NEW_FIELD("HardcoreUnlocks"),
    134     RC_JSON_NEW_FIELD("ServerNow")
    135   };
    136 
    137   rc_json_field_t unlock_entry_fields[] = {
    138     RC_JSON_NEW_FIELD("ID"),
    139     RC_JSON_NEW_FIELD("When")
    140   };
    141 
    142   memset(response, 0, sizeof(*response));
    143   rc_buffer_init(&response->response.buffer);
    144 
    145   result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
    146   if (result != RC_OK || !response->response.succeeded)
    147     return result;
    148 
    149   if (rc_json_get_optional_array(&response->num_unlocks, &array_field, &fields[2], "Unlocks") && response->num_unlocks) {
    150     response->unlocks = (rc_api_unlock_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_unlocks * sizeof(rc_api_unlock_entry_t));
    151     if (!response->unlocks)
    152       return RC_OUT_OF_MEMORY;
    153 
    154     memset(&iterator, 0, sizeof(iterator));
    155     iterator.json = array_field.value_start;
    156     iterator.end = array_field.value_end;
    157 
    158     unlock = response->unlocks;
    159     while (rc_json_get_array_entry_object(unlock_entry_fields, sizeof(unlock_entry_fields) / sizeof(unlock_entry_fields[0]), &iterator)) {
    160       if (!rc_json_get_required_unum(&unlock->achievement_id, &response->response, &unlock_entry_fields[0], "ID"))
    161         return RC_MISSING_VALUE;
    162       if (!rc_json_get_required_unum(&timet, &response->response, &unlock_entry_fields[1], "When"))
    163         return RC_MISSING_VALUE;
    164       unlock->when = (time_t)timet;
    165 
    166       ++unlock;
    167     }
    168   }
    169 
    170   if (rc_json_get_optional_array(&response->num_hardcore_unlocks, &array_field, &fields[3], "HardcoreUnlocks") && response->num_hardcore_unlocks) {
    171     response->hardcore_unlocks = (rc_api_unlock_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_hardcore_unlocks * sizeof(rc_api_unlock_entry_t));
    172     if (!response->hardcore_unlocks)
    173       return RC_OUT_OF_MEMORY;
    174 
    175     memset(&iterator, 0, sizeof(iterator));
    176     iterator.json = array_field.value_start;
    177     iterator.end = array_field.value_end;
    178 
    179     unlock = response->hardcore_unlocks;
    180     while (rc_json_get_array_entry_object(unlock_entry_fields, sizeof(unlock_entry_fields) / sizeof(unlock_entry_fields[0]), &iterator)) {
    181       if (!rc_json_get_required_unum(&unlock->achievement_id, &response->response, &unlock_entry_fields[0], "ID"))
    182         return RC_MISSING_VALUE;
    183       if (!rc_json_get_required_unum(&timet, &response->response, &unlock_entry_fields[1], "When"))
    184         return RC_MISSING_VALUE;
    185       unlock->when = (time_t)timet;
    186 
    187       ++unlock;
    188     }
    189   }
    190 
    191   rc_json_get_optional_unum(&timet, &fields[4], "ServerNow", 0);
    192   response->server_now = (time_t)timet;
    193 
    194   return RC_OK;
    195 }
    196 
    197 void rc_api_destroy_start_session_response(rc_api_start_session_response_t* response) {
    198   rc_buffer_destroy(&response->response.buffer);
    199 }
    200 
    201 /* --- Fetch User Unlocks --- */
    202 
    203 int rc_api_init_fetch_user_unlocks_request(rc_api_request_t* request, const rc_api_fetch_user_unlocks_request_t* api_params) {
    204   rc_api_url_builder_t builder;
    205 
    206   rc_api_url_build_dorequest_url(request);
    207 
    208   rc_url_builder_init(&builder, &request->buffer, 48);
    209   if (rc_api_url_build_dorequest(&builder, "unlocks", api_params->username, api_params->api_token)) {
    210     rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
    211     rc_url_builder_append_unum_param(&builder, "h", api_params->hardcore ? 1 : 0);
    212     request->post_data = rc_url_builder_finalize(&builder);
    213     request->content_type = RC_CONTENT_TYPE_URLENCODED;
    214   }
    215 
    216   return builder.result;
    217 }
    218 
    219 int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response, const char* server_response) {
    220   rc_api_server_response_t response_obj;
    221 
    222   memset(&response_obj, 0, sizeof(response_obj));
    223   response_obj.body = server_response;
    224   response_obj.body_length = rc_json_get_object_string_length(server_response);
    225 
    226   return rc_api_process_fetch_user_unlocks_server_response(response, &response_obj);
    227 }
    228 
    229 int rc_api_process_fetch_user_unlocks_server_response(rc_api_fetch_user_unlocks_response_t* response, const rc_api_server_response_t* server_response) {
    230   int result;
    231   rc_json_field_t fields[] = {
    232     RC_JSON_NEW_FIELD("Success"),
    233     RC_JSON_NEW_FIELD("Error"),
    234     RC_JSON_NEW_FIELD("UserUnlocks")
    235     /* unused fields
    236     RC_JSON_NEW_FIELD("GameID"),
    237     RC_JSON_NEW_FIELD("HardcoreMode")
    238      * unused fields */
    239   };
    240 
    241   memset(response, 0, sizeof(*response));
    242   rc_buffer_init(&response->response.buffer);
    243 
    244   result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
    245   if (result != RC_OK || !response->response.succeeded)
    246     return result;
    247 
    248   result = rc_json_get_required_unum_array(&response->achievement_ids, &response->num_achievement_ids, &response->response, &fields[2], "UserUnlocks");
    249   return result;
    250 }
    251 
    252 void rc_api_destroy_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response) {
    253   rc_buffer_destroy(&response->response.buffer);
    254 }