gemba 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/THIRD_PARTY_NOTICES +37 -2
- data/assets/placeholder_boxart.png +0 -0
- data/bin/gemba +2 -2
- data/ext/gemba/extconf.rb +23 -1
- data/ext/gemba/gemba_ext.c +436 -2
- data/ext/gemba/gemba_ext.h +2 -0
- data/gemba.gemspec +5 -3
- data/lib/gemba/achievements/achievement.rb +23 -0
- data/lib/gemba/achievements/backend.rb +186 -0
- data/lib/gemba/achievements/cache.rb +70 -0
- data/lib/gemba/achievements/credentials_presenter.rb +142 -0
- data/lib/gemba/achievements/fake_backend.rb +205 -0
- data/lib/gemba/achievements/null_backend.rb +11 -0
- data/lib/gemba/achievements/offline_backend.rb +168 -0
- data/lib/gemba/achievements/retro_achievements/backend.rb +453 -0
- data/lib/gemba/achievements/retro_achievements/cli_sync_requester.rb +64 -0
- data/lib/gemba/achievements/retro_achievements/ping_worker.rb +27 -0
- data/lib/gemba/achievements.rb +19 -0
- data/lib/gemba/achievements_window.rb +556 -0
- data/lib/gemba/app_controller.rb +1015 -0
- data/lib/gemba/bios.rb +54 -0
- data/lib/gemba/boxart_fetcher/libretro_backend.rb +39 -0
- data/lib/gemba/boxart_fetcher/null_backend.rb +12 -0
- data/lib/gemba/boxart_fetcher.rb +79 -0
- data/lib/gemba/bus_emitter.rb +13 -0
- data/lib/gemba/child_window.rb +24 -1
- data/lib/gemba/cli/commands/config_cmd.rb +83 -0
- data/lib/gemba/cli/commands/decode.rb +154 -0
- data/lib/gemba/cli/commands/patch.rb +78 -0
- data/lib/gemba/cli/commands/play.rb +78 -0
- data/lib/gemba/cli/commands/record.rb +114 -0
- data/lib/gemba/cli/commands/replay.rb +161 -0
- data/lib/gemba/cli/commands/retro_achievements.rb +213 -0
- data/lib/gemba/cli/commands/version.rb +22 -0
- data/lib/gemba/cli.rb +52 -364
- data/lib/gemba/config.rb +134 -1
- data/lib/gemba/data/gb_games.json +1 -0
- data/lib/gemba/data/gb_md5.json +1 -0
- data/lib/gemba/data/gba_games.json +1 -0
- data/lib/gemba/data/gba_md5.json +1 -0
- data/lib/gemba/data/gbc_games.json +1 -0
- data/lib/gemba/data/gbc_md5.json +1 -0
- data/lib/gemba/emulator_frame.rb +1060 -0
- data/lib/gemba/event_bus.rb +48 -0
- data/lib/gemba/frame_stack.rb +60 -0
- data/lib/gemba/game_index.rb +84 -0
- data/lib/gemba/game_picker_frame.rb +268 -0
- data/lib/gemba/gamepad_map.rb +103 -0
- data/lib/gemba/headless.rb +6 -5
- data/lib/gemba/headless_player.rb +33 -3
- data/lib/gemba/help_window.rb +61 -0
- data/lib/gemba/hotkey_map.rb +3 -1
- data/lib/gemba/input_recorder.rb +107 -0
- data/lib/gemba/input_replayer.rb +119 -0
- data/lib/gemba/keyboard_map.rb +90 -0
- data/lib/gemba/locales/en.yml +97 -5
- data/lib/gemba/locales/ja.yml +97 -5
- data/lib/gemba/main_window.rb +56 -0
- data/lib/gemba/modal_stack.rb +81 -0
- data/lib/gemba/patcher_window.rb +223 -0
- data/lib/gemba/platform/gb.rb +21 -0
- data/lib/gemba/platform/gba.rb +21 -0
- data/lib/gemba/platform/gbc.rb +23 -0
- data/lib/gemba/platform.rb +20 -0
- data/lib/gemba/platform_open.rb +19 -0
- data/lib/gemba/recorder.rb +4 -3
- data/lib/gemba/replay_player.rb +691 -0
- data/lib/gemba/rom_info.rb +57 -0
- data/lib/gemba/rom_info_window.rb +16 -3
- data/lib/gemba/rom_library.rb +106 -0
- data/lib/gemba/rom_overrides.rb +47 -0
- data/lib/gemba/rom_patcher/bps.rb +161 -0
- data/lib/gemba/rom_patcher/ips.rb +101 -0
- data/lib/gemba/rom_patcher/ups.rb +118 -0
- data/lib/gemba/rom_patcher.rb +109 -0
- data/lib/gemba/{rom_loader.rb → rom_resolver.rb} +7 -6
- data/lib/gemba/runtime.rb +59 -26
- data/lib/gemba/save_state_manager.rb +4 -7
- data/lib/gemba/save_state_picker.rb +17 -4
- data/lib/gemba/session_logger.rb +64 -0
- data/lib/gemba/settings/audio_tab.rb +77 -0
- data/lib/gemba/settings/gamepad_tab.rb +351 -0
- data/lib/gemba/settings/hotkeys_tab.rb +259 -0
- data/lib/gemba/settings/paths.rb +11 -0
- data/lib/gemba/settings/recording_tab.rb +83 -0
- data/lib/gemba/settings/save_states_tab.rb +91 -0
- data/lib/gemba/settings/system_tab.rb +362 -0
- data/lib/gemba/settings/video_tab.rb +318 -0
- data/lib/gemba/settings_window.rb +162 -1036
- data/lib/gemba/version.rb +1 -1
- data/lib/gemba/virtual_keyboard.rb +19 -0
- data/lib/gemba.rb +2 -12
- data/test/achievements_window/test_bulk_sync.rb +218 -0
- data/test/achievements_window/test_bus_events.rb +125 -0
- data/test/achievements_window/test_close_confirmation.rb +201 -0
- data/test/achievements_window/test_initial_state.rb +164 -0
- data/test/achievements_window/test_sorting.rb +227 -0
- data/test/achievements_window/test_tree_rendering.rb +133 -0
- data/test/fixtures/fake_bios.bin +0 -0
- data/test/fixtures/pong.gba +0 -0
- data/test/fixtures/test.gb +0 -0
- data/test/fixtures/test.gbc +0 -0
- data/test/fixtures/test_quicksave.ss +0 -0
- data/test/screenshots/no_focus.png +0 -0
- data/test/shared/teek_test_worker.rb +17 -1
- data/test/shared/tk_test_helper.rb +91 -4
- data/test/support/achievements_window_helpers.rb +18 -0
- data/test/support/fake_core.rb +25 -0
- data/test/support/fake_ra_runtime.rb +74 -0
- data/test/support/fake_requester.rb +68 -0
- data/test/support/player_helpers.rb +20 -5
- data/test/test_achievement.rb +32 -0
- data/test/{test_player.rb → test_app_controller.rb} +353 -85
- data/test/test_bios.rb +123 -0
- data/test/test_boxart_fetcher.rb +150 -0
- data/test/test_cli.rb +17 -265
- data/test/test_cli_config.rb +64 -0
- data/test/test_cli_decode.rb +97 -0
- data/test/test_cli_patch.rb +58 -0
- data/test/test_cli_play.rb +213 -0
- data/test/test_cli_ra.rb +175 -0
- data/test/test_cli_record.rb +69 -0
- data/test/test_cli_replay.rb +72 -0
- data/test/test_cli_sync_requester.rb +152 -0
- data/test/test_cli_version.rb +27 -0
- data/test/test_config.rb +2 -3
- data/test/test_config_ra.rb +69 -0
- data/test/test_core.rb +62 -1
- data/test/test_credentials_presenter.rb +192 -0
- data/test/test_event_bus.rb +100 -0
- data/test/test_fake_backend_achievements.rb +130 -0
- data/test/test_fake_backend_auth.rb +68 -0
- data/test/test_game_index.rb +77 -0
- data/test/test_game_picker_frame.rb +310 -0
- data/test/test_gamepad_map.rb +1 -3
- data/test/test_headless_player.rb +17 -3
- data/test/test_help_window.rb +82 -0
- data/test/test_hotkey_map.rb +22 -1
- data/test/test_input_recorder.rb +179 -0
- data/test/test_input_replay_determinism.rb +113 -0
- data/test/test_input_replayer.rb +162 -0
- data/test/test_keyboard_map.rb +1 -3
- data/test/test_libretro_backend.rb +41 -0
- data/test/test_locale.rb +1 -1
- data/test/test_logging.rb +123 -0
- data/test/test_null_backend.rb +42 -0
- data/test/test_offline_backend.rb +116 -0
- data/test/test_overlay_renderer.rb +1 -1
- data/test/test_platform.rb +149 -0
- data/test/test_ra_backend.rb +313 -0
- data/test/test_ra_backend_unlock_gate.rb +56 -0
- data/test/test_recorder.rb +0 -3
- data/test/test_replay_player.rb +316 -0
- data/test/test_rom_info.rb +149 -0
- data/test/test_rom_overrides.rb +86 -0
- data/test/test_rom_patcher.rb +382 -0
- data/test/{test_rom_loader.rb → test_rom_resolver.rb} +25 -26
- data/test/test_save_state_manager.rb +2 -4
- data/test/test_settings_audio.rb +107 -0
- data/test/test_settings_hotkeys.rb +83 -66
- data/test/test_settings_recording.rb +49 -0
- data/test/test_settings_save_states.rb +97 -0
- data/test/test_settings_system.rb +133 -0
- data/test/test_settings_video.rb +450 -0
- data/test/test_settings_window.rb +76 -507
- data/test/test_tip_service.rb +6 -6
- data/test/test_toast_overlay.rb +1 -1
- data/test/test_virtual_events.rb +156 -0
- data/test/test_virtual_keyboard.rb +1 -1
- data/vendor/rcheevos/CHANGELOG.md +495 -0
- data/vendor/rcheevos/LICENSE +21 -0
- data/vendor/rcheevos/Package.swift +33 -0
- data/vendor/rcheevos/README.md +67 -0
- data/vendor/rcheevos/include/module.modulemap +70 -0
- data/vendor/rcheevos/include/rc_api_editor.h +296 -0
- data/vendor/rcheevos/include/rc_api_info.h +280 -0
- data/vendor/rcheevos/include/rc_api_request.h +77 -0
- data/vendor/rcheevos/include/rc_api_runtime.h +417 -0
- data/vendor/rcheevos/include/rc_api_user.h +262 -0
- data/vendor/rcheevos/include/rc_client.h +877 -0
- data/vendor/rcheevos/include/rc_client_raintegration.h +101 -0
- data/vendor/rcheevos/include/rc_consoles.h +138 -0
- data/vendor/rcheevos/include/rc_error.h +59 -0
- data/vendor/rcheevos/include/rc_export.h +100 -0
- data/vendor/rcheevos/include/rc_hash.h +200 -0
- data/vendor/rcheevos/include/rc_runtime.h +148 -0
- data/vendor/rcheevos/include/rc_runtime_types.h +452 -0
- data/vendor/rcheevos/include/rc_util.h +51 -0
- data/vendor/rcheevos/include/rcheevos.h +8 -0
- data/vendor/rcheevos/src/rapi/rc_api_common.c +1379 -0
- data/vendor/rcheevos/src/rapi/rc_api_common.h +88 -0
- data/vendor/rcheevos/src/rapi/rc_api_editor.c +625 -0
- data/vendor/rcheevos/src/rapi/rc_api_info.c +587 -0
- data/vendor/rcheevos/src/rapi/rc_api_runtime.c +901 -0
- data/vendor/rcheevos/src/rapi/rc_api_user.c +483 -0
- data/vendor/rcheevos/src/rc_client.c +6941 -0
- data/vendor/rcheevos/src/rc_client_external.c +281 -0
- data/vendor/rcheevos/src/rc_client_external.h +177 -0
- data/vendor/rcheevos/src/rc_client_external_versions.h +171 -0
- data/vendor/rcheevos/src/rc_client_internal.h +409 -0
- data/vendor/rcheevos/src/rc_client_raintegration.c +566 -0
- data/vendor/rcheevos/src/rc_client_raintegration_internal.h +61 -0
- data/vendor/rcheevos/src/rc_client_types.natvis +396 -0
- data/vendor/rcheevos/src/rc_compat.c +251 -0
- data/vendor/rcheevos/src/rc_compat.h +121 -0
- data/vendor/rcheevos/src/rc_libretro.c +915 -0
- data/vendor/rcheevos/src/rc_libretro.h +98 -0
- data/vendor/rcheevos/src/rc_util.c +199 -0
- data/vendor/rcheevos/src/rc_version.c +11 -0
- data/vendor/rcheevos/src/rc_version.h +32 -0
- data/vendor/rcheevos/src/rcheevos/alloc.c +312 -0
- data/vendor/rcheevos/src/rcheevos/condition.c +754 -0
- data/vendor/rcheevos/src/rcheevos/condset.c +777 -0
- data/vendor/rcheevos/src/rcheevos/consoleinfo.c +1215 -0
- data/vendor/rcheevos/src/rcheevos/format.c +330 -0
- data/vendor/rcheevos/src/rcheevos/lboard.c +287 -0
- data/vendor/rcheevos/src/rcheevos/memref.c +805 -0
- data/vendor/rcheevos/src/rcheevos/operand.c +607 -0
- data/vendor/rcheevos/src/rcheevos/rc_internal.h +390 -0
- data/vendor/rcheevos/src/rcheevos/rc_runtime_types.natvis +541 -0
- data/vendor/rcheevos/src/rcheevos/rc_validate.c +1406 -0
- data/vendor/rcheevos/src/rcheevos/rc_validate.h +18 -0
- data/vendor/rcheevos/src/rcheevos/richpresence.c +922 -0
- data/vendor/rcheevos/src/rcheevos/runtime.c +852 -0
- data/vendor/rcheevos/src/rcheevos/runtime_progress.c +1073 -0
- data/vendor/rcheevos/src/rcheevos/trigger.c +344 -0
- data/vendor/rcheevos/src/rcheevos/value.c +935 -0
- data/vendor/rcheevos/src/rhash/aes.c +480 -0
- data/vendor/rcheevos/src/rhash/aes.h +49 -0
- data/vendor/rcheevos/src/rhash/cdreader.c +838 -0
- data/vendor/rcheevos/src/rhash/hash.c +1402 -0
- data/vendor/rcheevos/src/rhash/hash_disc.c +1340 -0
- data/vendor/rcheevos/src/rhash/hash_encrypted.c +566 -0
- data/vendor/rcheevos/src/rhash/hash_rom.c +426 -0
- data/vendor/rcheevos/src/rhash/hash_zip.c +460 -0
- data/vendor/rcheevos/src/rhash/md5.c +382 -0
- data/vendor/rcheevos/src/rhash/md5.h +91 -0
- data/vendor/rcheevos/src/rhash/rc_hash_internal.h +116 -0
- data/vendor/rcheevos/test/libretro.h +205 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_common.c +941 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_editor.c +931 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_info.c +545 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_runtime.c +2213 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_user.c +998 -0
- data/vendor/rcheevos/test/rcheevos/mock_memory.h +32 -0
- data/vendor/rcheevos/test/rcheevos/test_condition.c +570 -0
- data/vendor/rcheevos/test/rcheevos/test_condset.c +5170 -0
- data/vendor/rcheevos/test/rcheevos/test_consoleinfo.c +203 -0
- data/vendor/rcheevos/test/rcheevos/test_format.c +112 -0
- data/vendor/rcheevos/test/rcheevos/test_lboard.c +746 -0
- data/vendor/rcheevos/test/rcheevos/test_memref.c +520 -0
- data/vendor/rcheevos/test/rcheevos/test_operand.c +692 -0
- data/vendor/rcheevos/test/rcheevos/test_rc_validate.c +502 -0
- data/vendor/rcheevos/test/rcheevos/test_richpresence.c +1564 -0
- data/vendor/rcheevos/test/rcheevos/test_runtime.c +1667 -0
- data/vendor/rcheevos/test/rcheevos/test_runtime_progress.c +1821 -0
- data/vendor/rcheevos/test/rcheevos/test_timing.c +166 -0
- data/vendor/rcheevos/test/rcheevos/test_trigger.c +2521 -0
- data/vendor/rcheevos/test/rcheevos/test_value.c +870 -0
- data/vendor/rcheevos/test/rcheevos-test.sln +46 -0
- data/vendor/rcheevos/test/rcheevos-test.vcxproj +239 -0
- data/vendor/rcheevos/test/rcheevos-test.vcxproj.filters +335 -0
- data/vendor/rcheevos/test/rhash/data.c +657 -0
- data/vendor/rcheevos/test/rhash/data.h +32 -0
- data/vendor/rcheevos/test/rhash/mock_filereader.c +236 -0
- data/vendor/rcheevos/test/rhash/mock_filereader.h +31 -0
- data/vendor/rcheevos/test/rhash/test_cdreader.c +920 -0
- data/vendor/rcheevos/test/rhash/test_hash.c +310 -0
- data/vendor/rcheevos/test/rhash/test_hash_disc.c +1450 -0
- data/vendor/rcheevos/test/rhash/test_hash_rom.c +899 -0
- data/vendor/rcheevos/test/rhash/test_hash_zip.c +551 -0
- data/vendor/rcheevos/test/test.c +113 -0
- data/vendor/rcheevos/test/test_framework.h +205 -0
- data/vendor/rcheevos/test/test_rc_client.c +10509 -0
- data/vendor/rcheevos/test/test_rc_client_external.c +2197 -0
- data/vendor/rcheevos/test/test_rc_client_raintegration.c +441 -0
- data/vendor/rcheevos/test/test_rc_libretro.c +952 -0
- data/vendor/rcheevos/test/test_types.natvis +9 -0
- data/vendor/rcheevos/validator/validator.c +658 -0
- data/vendor/rcheevos/validator/validator.vcxproj +152 -0
- data/vendor/rcheevos/validator/validator.vcxproj.filters +82 -0
- metadata +274 -11
- data/lib/gemba/input_mappings.rb +0 -214
- data/lib/gemba/player.rb +0 -1525
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
#include "rc_api_info.h"
|
|
2
|
+
#include "rc_api_common.h"
|
|
3
|
+
#include "rc_api_runtime.h"
|
|
4
|
+
|
|
5
|
+
#include "rc_runtime_types.h"
|
|
6
|
+
|
|
7
|
+
#include "../rc_compat.h"
|
|
8
|
+
|
|
9
|
+
#include <stdlib.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
|
|
12
|
+
/* --- Fetch Achievement Info --- */
|
|
13
|
+
|
|
14
|
+
int rc_api_init_fetch_achievement_info_request(rc_api_request_t* request, const rc_api_fetch_achievement_info_request_t* api_params) {
|
|
15
|
+
return rc_api_init_fetch_achievement_info_request_hosted(request, api_params, &g_host);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
int rc_api_init_fetch_achievement_info_request_hosted(rc_api_request_t* request,
|
|
19
|
+
const rc_api_fetch_achievement_info_request_t* api_params,
|
|
20
|
+
const rc_api_host_t* host) {
|
|
21
|
+
rc_api_url_builder_t builder;
|
|
22
|
+
|
|
23
|
+
rc_api_url_build_dorequest_url(request, host);
|
|
24
|
+
|
|
25
|
+
if (api_params->achievement_id == 0)
|
|
26
|
+
return RC_INVALID_STATE;
|
|
27
|
+
|
|
28
|
+
rc_url_builder_init(&builder, &request->buffer, 48);
|
|
29
|
+
if (rc_api_url_build_dorequest(&builder, "achievementwondata", api_params->username, api_params->api_token)) {
|
|
30
|
+
rc_url_builder_append_unum_param(&builder, "a", api_params->achievement_id);
|
|
31
|
+
|
|
32
|
+
if (api_params->friends_only)
|
|
33
|
+
rc_url_builder_append_unum_param(&builder, "f", 1);
|
|
34
|
+
if (api_params->first_entry > 1)
|
|
35
|
+
rc_url_builder_append_unum_param(&builder, "o", api_params->first_entry - 1); /* number of entries to skip */
|
|
36
|
+
rc_url_builder_append_unum_param(&builder, "c", api_params->count);
|
|
37
|
+
|
|
38
|
+
request->post_data = rc_url_builder_finalize(&builder);
|
|
39
|
+
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return builder.result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response, const char* server_response) {
|
|
46
|
+
rc_api_server_response_t response_obj;
|
|
47
|
+
|
|
48
|
+
memset(&response_obj, 0, sizeof(response_obj));
|
|
49
|
+
response_obj.body = server_response;
|
|
50
|
+
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
|
51
|
+
|
|
52
|
+
return rc_api_process_fetch_achievement_info_server_response(response, &response_obj);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
int rc_api_process_fetch_achievement_info_server_response(rc_api_fetch_achievement_info_response_t* response, const rc_api_server_response_t* server_response) {
|
|
56
|
+
rc_api_achievement_awarded_entry_t* entry;
|
|
57
|
+
rc_json_field_t array_field;
|
|
58
|
+
rc_json_iterator_t iterator;
|
|
59
|
+
uint32_t timet;
|
|
60
|
+
int result;
|
|
61
|
+
|
|
62
|
+
rc_json_field_t fields[] = {
|
|
63
|
+
RC_JSON_NEW_FIELD("Success"),
|
|
64
|
+
RC_JSON_NEW_FIELD("Error"),
|
|
65
|
+
RC_JSON_NEW_FIELD("AchievementID"),
|
|
66
|
+
RC_JSON_NEW_FIELD("Response")
|
|
67
|
+
/* unused fields
|
|
68
|
+
RC_JSON_NEW_FIELD("Offset"),
|
|
69
|
+
RC_JSON_NEW_FIELD("Count"),
|
|
70
|
+
RC_JSON_NEW_FIELD("FriendsOnly")
|
|
71
|
+
* unused fields */
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
rc_json_field_t response_fields[] = {
|
|
75
|
+
RC_JSON_NEW_FIELD("NumEarned"),
|
|
76
|
+
RC_JSON_NEW_FIELD("TotalPlayers"),
|
|
77
|
+
RC_JSON_NEW_FIELD("GameID"),
|
|
78
|
+
RC_JSON_NEW_FIELD("RecentWinner") /* array */
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
rc_json_field_t entry_fields[] = {
|
|
82
|
+
RC_JSON_NEW_FIELD("User"),
|
|
83
|
+
RC_JSON_NEW_FIELD("DateAwarded"),
|
|
84
|
+
RC_JSON_NEW_FIELD("AvatarUrl")
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
memset(response, 0, sizeof(*response));
|
|
88
|
+
rc_buffer_init(&response->response.buffer);
|
|
89
|
+
|
|
90
|
+
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
|
91
|
+
if (result != RC_OK)
|
|
92
|
+
return result;
|
|
93
|
+
|
|
94
|
+
if (!rc_json_get_required_unum(&response->id, &response->response, &fields[2], "AchievementID"))
|
|
95
|
+
return RC_MISSING_VALUE;
|
|
96
|
+
if (!rc_json_get_required_object(response_fields, sizeof(response_fields) / sizeof(response_fields[0]), &response->response, &fields[3], "Response"))
|
|
97
|
+
return RC_MISSING_VALUE;
|
|
98
|
+
|
|
99
|
+
if (!rc_json_get_required_unum(&response->num_awarded, &response->response, &response_fields[0], "NumEarned"))
|
|
100
|
+
return RC_MISSING_VALUE;
|
|
101
|
+
if (!rc_json_get_required_unum(&response->num_players, &response->response, &response_fields[1], "TotalPlayers"))
|
|
102
|
+
return RC_MISSING_VALUE;
|
|
103
|
+
if (!rc_json_get_required_unum(&response->game_id, &response->response, &response_fields[2], "GameID"))
|
|
104
|
+
return RC_MISSING_VALUE;
|
|
105
|
+
|
|
106
|
+
if (!rc_json_get_required_array(&response->num_recently_awarded, &array_field, &response->response, &response_fields[3], "RecentWinner"))
|
|
107
|
+
return RC_MISSING_VALUE;
|
|
108
|
+
|
|
109
|
+
if (response->num_recently_awarded) {
|
|
110
|
+
response->recently_awarded = (rc_api_achievement_awarded_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_recently_awarded * sizeof(rc_api_achievement_awarded_entry_t));
|
|
111
|
+
if (!response->recently_awarded)
|
|
112
|
+
return RC_OUT_OF_MEMORY;
|
|
113
|
+
|
|
114
|
+
memset(&iterator, 0, sizeof(iterator));
|
|
115
|
+
iterator.json = array_field.value_start;
|
|
116
|
+
iterator.end = array_field.value_end;
|
|
117
|
+
|
|
118
|
+
entry = response->recently_awarded;
|
|
119
|
+
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
|
|
120
|
+
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
|
|
121
|
+
return RC_MISSING_VALUE;
|
|
122
|
+
|
|
123
|
+
if (!rc_json_get_required_unum(&timet, &response->response, &entry_fields[1], "DateAwarded"))
|
|
124
|
+
return RC_MISSING_VALUE;
|
|
125
|
+
entry->awarded = (time_t)timet;
|
|
126
|
+
|
|
127
|
+
rc_json_get_optional_string(&entry->avatar_url, &response->response, &entry_fields[2], "AvatarUrl", NULL);
|
|
128
|
+
if (!entry->avatar_url)
|
|
129
|
+
entry->avatar_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_USER, entry->username);
|
|
130
|
+
|
|
131
|
+
++entry;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return RC_OK;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
void rc_api_destroy_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response) {
|
|
139
|
+
rc_buffer_destroy(&response->response.buffer);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* --- Fetch Leaderboard Info --- */
|
|
143
|
+
|
|
144
|
+
int rc_api_init_fetch_leaderboard_info_request(rc_api_request_t* request, const rc_api_fetch_leaderboard_info_request_t* api_params) {
|
|
145
|
+
return rc_api_init_fetch_leaderboard_info_request_hosted(request, api_params, &g_host);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
int rc_api_init_fetch_leaderboard_info_request_hosted(rc_api_request_t* request,
|
|
149
|
+
const rc_api_fetch_leaderboard_info_request_t* api_params,
|
|
150
|
+
const rc_api_host_t* host) {
|
|
151
|
+
rc_api_url_builder_t builder;
|
|
152
|
+
|
|
153
|
+
rc_api_url_build_dorequest_url(request, host);
|
|
154
|
+
|
|
155
|
+
if (api_params->leaderboard_id == 0)
|
|
156
|
+
return RC_INVALID_STATE;
|
|
157
|
+
|
|
158
|
+
rc_url_builder_init(&builder, &request->buffer, 48);
|
|
159
|
+
rc_url_builder_append_str_param(&builder, "r", "lbinfo");
|
|
160
|
+
rc_url_builder_append_unum_param(&builder, "i", api_params->leaderboard_id);
|
|
161
|
+
|
|
162
|
+
if (api_params->username)
|
|
163
|
+
rc_url_builder_append_str_param(&builder, "u", api_params->username);
|
|
164
|
+
else if (api_params->first_entry > 1)
|
|
165
|
+
rc_url_builder_append_unum_param(&builder, "o", api_params->first_entry - 1); /* number of entries to skip */
|
|
166
|
+
|
|
167
|
+
rc_url_builder_append_unum_param(&builder, "c", api_params->count);
|
|
168
|
+
request->post_data = rc_url_builder_finalize(&builder);
|
|
169
|
+
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
|
170
|
+
|
|
171
|
+
return builder.result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response, const char* server_response) {
|
|
175
|
+
rc_api_server_response_t response_obj;
|
|
176
|
+
|
|
177
|
+
memset(&response_obj, 0, sizeof(response_obj));
|
|
178
|
+
response_obj.body = server_response;
|
|
179
|
+
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
|
180
|
+
|
|
181
|
+
return rc_api_process_fetch_leaderboard_info_server_response(response, &response_obj);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
int rc_api_process_fetch_leaderboard_info_server_response(rc_api_fetch_leaderboard_info_response_t* response, const rc_api_server_response_t* server_response) {
|
|
185
|
+
rc_api_lboard_info_entry_t* entry;
|
|
186
|
+
rc_json_field_t array_field;
|
|
187
|
+
rc_json_iterator_t iterator;
|
|
188
|
+
uint32_t timet;
|
|
189
|
+
int result;
|
|
190
|
+
size_t len;
|
|
191
|
+
char format[16];
|
|
192
|
+
|
|
193
|
+
rc_json_field_t fields[] = {
|
|
194
|
+
RC_JSON_NEW_FIELD("Success"),
|
|
195
|
+
RC_JSON_NEW_FIELD("Error"),
|
|
196
|
+
RC_JSON_NEW_FIELD("LeaderboardData")
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
rc_json_field_t leaderboarddata_fields[] = {
|
|
200
|
+
RC_JSON_NEW_FIELD("LBID"),
|
|
201
|
+
RC_JSON_NEW_FIELD("LBFormat"),
|
|
202
|
+
RC_JSON_NEW_FIELD("LowerIsBetter"),
|
|
203
|
+
RC_JSON_NEW_FIELD("LBTitle"),
|
|
204
|
+
RC_JSON_NEW_FIELD("LBDesc"),
|
|
205
|
+
RC_JSON_NEW_FIELD("LBMem"),
|
|
206
|
+
RC_JSON_NEW_FIELD("GameID"),
|
|
207
|
+
RC_JSON_NEW_FIELD("LBAuthor"),
|
|
208
|
+
RC_JSON_NEW_FIELD("LBCreated"),
|
|
209
|
+
RC_JSON_NEW_FIELD("LBUpdated"),
|
|
210
|
+
RC_JSON_NEW_FIELD("Entries"), /* array */
|
|
211
|
+
RC_JSON_NEW_FIELD("TotalEntries")
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
rc_json_field_t entry_fields[] = {
|
|
215
|
+
RC_JSON_NEW_FIELD("User"),
|
|
216
|
+
RC_JSON_NEW_FIELD("Rank"),
|
|
217
|
+
RC_JSON_NEW_FIELD("Index"),
|
|
218
|
+
RC_JSON_NEW_FIELD("Score"),
|
|
219
|
+
RC_JSON_NEW_FIELD("DateSubmitted"),
|
|
220
|
+
RC_JSON_NEW_FIELD("AvatarUrl")
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
memset(response, 0, sizeof(*response));
|
|
224
|
+
rc_buffer_init(&response->response.buffer);
|
|
225
|
+
|
|
226
|
+
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
|
227
|
+
if (result != RC_OK)
|
|
228
|
+
return result;
|
|
229
|
+
|
|
230
|
+
if (!rc_json_get_required_object(leaderboarddata_fields, sizeof(leaderboarddata_fields) / sizeof(leaderboarddata_fields[0]), &response->response, &fields[2], "LeaderboardData"))
|
|
231
|
+
return RC_MISSING_VALUE;
|
|
232
|
+
|
|
233
|
+
if (!rc_json_get_required_unum(&response->id, &response->response, &leaderboarddata_fields[0], "LBID"))
|
|
234
|
+
return RC_MISSING_VALUE;
|
|
235
|
+
if (!rc_json_get_required_unum(&response->lower_is_better, &response->response, &leaderboarddata_fields[2], "LowerIsBetter"))
|
|
236
|
+
return RC_MISSING_VALUE;
|
|
237
|
+
if (!rc_json_get_required_string(&response->title, &response->response, &leaderboarddata_fields[3], "LBTitle"))
|
|
238
|
+
return RC_MISSING_VALUE;
|
|
239
|
+
if (!rc_json_get_required_string(&response->description, &response->response, &leaderboarddata_fields[4], "LBDesc"))
|
|
240
|
+
return RC_MISSING_VALUE;
|
|
241
|
+
if (!rc_json_get_required_string(&response->definition, &response->response, &leaderboarddata_fields[5], "LBMem"))
|
|
242
|
+
return RC_MISSING_VALUE;
|
|
243
|
+
if (!rc_json_get_required_unum(&response->game_id, &response->response, &leaderboarddata_fields[6], "GameID"))
|
|
244
|
+
return RC_MISSING_VALUE;
|
|
245
|
+
if (!rc_json_get_required_string(&response->author, &response->response, &leaderboarddata_fields[7], "LBAuthor"))
|
|
246
|
+
return RC_MISSING_VALUE;
|
|
247
|
+
if (!rc_json_get_required_datetime(&response->created, &response->response, &leaderboarddata_fields[8], "LBCreated"))
|
|
248
|
+
return RC_MISSING_VALUE;
|
|
249
|
+
if (!rc_json_get_required_datetime(&response->updated, &response->response, &leaderboarddata_fields[9], "LBUpdated"))
|
|
250
|
+
return RC_MISSING_VALUE;
|
|
251
|
+
if (!rc_json_get_required_unum(&response->total_entries, &response->response, &leaderboarddata_fields[11], "TotalEntries"))
|
|
252
|
+
return RC_MISSING_VALUE;
|
|
253
|
+
|
|
254
|
+
if (!leaderboarddata_fields[1].value_end)
|
|
255
|
+
return RC_MISSING_VALUE;
|
|
256
|
+
len = leaderboarddata_fields[1].value_end - leaderboarddata_fields[1].value_start - 2;
|
|
257
|
+
if (len < sizeof(format) - 1) {
|
|
258
|
+
memcpy(format, leaderboarddata_fields[1].value_start + 1, len);
|
|
259
|
+
format[len] = '\0';
|
|
260
|
+
response->format = rc_parse_format(format);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
response->format = RC_FORMAT_VALUE;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &leaderboarddata_fields[10], "Entries"))
|
|
267
|
+
return RC_MISSING_VALUE;
|
|
268
|
+
|
|
269
|
+
if (response->num_entries) {
|
|
270
|
+
response->entries = (rc_api_lboard_info_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_lboard_info_entry_t));
|
|
271
|
+
if (!response->entries)
|
|
272
|
+
return RC_OUT_OF_MEMORY;
|
|
273
|
+
|
|
274
|
+
memset(&iterator, 0, sizeof(iterator));
|
|
275
|
+
iterator.json = array_field.value_start;
|
|
276
|
+
iterator.end = array_field.value_end;
|
|
277
|
+
|
|
278
|
+
entry = response->entries;
|
|
279
|
+
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
|
|
280
|
+
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
|
|
281
|
+
return RC_MISSING_VALUE;
|
|
282
|
+
|
|
283
|
+
if (!rc_json_get_required_unum(&entry->rank, &response->response, &entry_fields[1], "Rank"))
|
|
284
|
+
return RC_MISSING_VALUE;
|
|
285
|
+
|
|
286
|
+
if (!rc_json_get_required_unum(&entry->index, &response->response, &entry_fields[2], "Index"))
|
|
287
|
+
return RC_MISSING_VALUE;
|
|
288
|
+
|
|
289
|
+
if (!rc_json_get_required_num(&entry->score, &response->response, &entry_fields[3], "Score"))
|
|
290
|
+
return RC_MISSING_VALUE;
|
|
291
|
+
|
|
292
|
+
if (!rc_json_get_required_unum(&timet, &response->response, &entry_fields[4], "DateSubmitted"))
|
|
293
|
+
return RC_MISSING_VALUE;
|
|
294
|
+
entry->submitted = (time_t)timet;
|
|
295
|
+
|
|
296
|
+
rc_json_get_optional_string(&entry->avatar_url, &response->response, &entry_fields[5], "AvatarUrl", NULL);
|
|
297
|
+
if (!entry->avatar_url)
|
|
298
|
+
entry->avatar_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_USER, entry->username);
|
|
299
|
+
|
|
300
|
+
++entry;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return RC_OK;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
void rc_api_destroy_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response) {
|
|
308
|
+
rc_buffer_destroy(&response->response.buffer);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* --- Fetch Games List --- */
|
|
312
|
+
|
|
313
|
+
int rc_api_init_fetch_games_list_request(rc_api_request_t* request, const rc_api_fetch_games_list_request_t* api_params) {
|
|
314
|
+
return rc_api_init_fetch_games_list_request_hosted(request, api_params, &g_host);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
int rc_api_init_fetch_games_list_request_hosted(rc_api_request_t* request,
|
|
318
|
+
const rc_api_fetch_games_list_request_t* api_params,
|
|
319
|
+
const rc_api_host_t* host) {
|
|
320
|
+
rc_api_url_builder_t builder;
|
|
321
|
+
|
|
322
|
+
rc_api_url_build_dorequest_url(request, host);
|
|
323
|
+
|
|
324
|
+
if (api_params->console_id == 0)
|
|
325
|
+
return RC_INVALID_STATE;
|
|
326
|
+
|
|
327
|
+
rc_url_builder_init(&builder, &request->buffer, 48);
|
|
328
|
+
rc_url_builder_append_str_param(&builder, "r", "gameslist");
|
|
329
|
+
rc_url_builder_append_unum_param(&builder, "c", api_params->console_id);
|
|
330
|
+
|
|
331
|
+
request->post_data = rc_url_builder_finalize(&builder);
|
|
332
|
+
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
|
333
|
+
|
|
334
|
+
return builder.result;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t* response, const char* server_response) {
|
|
338
|
+
rc_api_server_response_t response_obj;
|
|
339
|
+
|
|
340
|
+
memset(&response_obj, 0, sizeof(response_obj));
|
|
341
|
+
response_obj.body = server_response;
|
|
342
|
+
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
|
343
|
+
|
|
344
|
+
return rc_api_process_fetch_games_list_server_response(response, &response_obj);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
int rc_api_process_fetch_games_list_server_response(rc_api_fetch_games_list_response_t* response, const rc_api_server_response_t* server_response) {
|
|
348
|
+
rc_api_game_list_entry_t* entry;
|
|
349
|
+
rc_json_iterator_t iterator;
|
|
350
|
+
rc_json_field_t field;
|
|
351
|
+
int result;
|
|
352
|
+
char* end;
|
|
353
|
+
|
|
354
|
+
rc_json_field_t fields[] = {
|
|
355
|
+
RC_JSON_NEW_FIELD("Success"),
|
|
356
|
+
RC_JSON_NEW_FIELD("Error"),
|
|
357
|
+
RC_JSON_NEW_FIELD("Response")
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
memset(response, 0, sizeof(*response));
|
|
361
|
+
rc_buffer_init(&response->response.buffer);
|
|
362
|
+
|
|
363
|
+
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
|
364
|
+
if (result != RC_OK)
|
|
365
|
+
return result;
|
|
366
|
+
|
|
367
|
+
if (!fields[2].value_start) {
|
|
368
|
+
/* call rc_json_get_required_object to generate the error message */
|
|
369
|
+
rc_json_get_required_object(NULL, 0, &response->response, &fields[2], "Response");
|
|
370
|
+
return RC_MISSING_VALUE;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
response->num_entries = fields[2].array_size;
|
|
374
|
+
rc_buffer_reserve(&response->response.buffer, response->num_entries * (32 + sizeof(rc_api_game_list_entry_t)));
|
|
375
|
+
|
|
376
|
+
response->entries = (rc_api_game_list_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_list_entry_t));
|
|
377
|
+
if (!response->entries)
|
|
378
|
+
return RC_OUT_OF_MEMORY;
|
|
379
|
+
|
|
380
|
+
memset(&iterator, 0, sizeof(iterator));
|
|
381
|
+
iterator.json = fields[2].value_start;
|
|
382
|
+
iterator.end = fields[2].value_end;
|
|
383
|
+
|
|
384
|
+
entry = response->entries;
|
|
385
|
+
while (rc_json_get_next_object_field(&iterator, &field)) {
|
|
386
|
+
entry->id = strtol(field.name, &end, 10);
|
|
387
|
+
|
|
388
|
+
field.name = "";
|
|
389
|
+
if (!rc_json_get_string(&entry->name, &response->response.buffer, &field, ""))
|
|
390
|
+
return RC_MISSING_VALUE;
|
|
391
|
+
|
|
392
|
+
++entry;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return RC_OK;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
void rc_api_destroy_fetch_games_list_response(rc_api_fetch_games_list_response_t* response) {
|
|
399
|
+
rc_buffer_destroy(&response->response.buffer);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/* --- Fetch Game Titles --- */
|
|
403
|
+
|
|
404
|
+
int rc_api_init_fetch_game_titles_request(rc_api_request_t* request, const rc_api_fetch_game_titles_request_t* api_params) {
|
|
405
|
+
return rc_api_init_fetch_game_titles_request_hosted(request, api_params, &g_host);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
int rc_api_init_fetch_game_titles_request_hosted(rc_api_request_t* request,
|
|
409
|
+
const rc_api_fetch_game_titles_request_t* api_params,
|
|
410
|
+
const rc_api_host_t* host) {
|
|
411
|
+
rc_api_url_builder_t builder;
|
|
412
|
+
char num[16];
|
|
413
|
+
uint32_t i;
|
|
414
|
+
|
|
415
|
+
rc_api_url_build_dorequest_url(request, host);
|
|
416
|
+
|
|
417
|
+
if (api_params->num_game_ids == 0)
|
|
418
|
+
return RC_INVALID_STATE;
|
|
419
|
+
|
|
420
|
+
rc_url_builder_init(&builder, &request->buffer, 48);
|
|
421
|
+
rc_url_builder_append_str_param(&builder, "r", "gameinfolist");
|
|
422
|
+
rc_url_builder_append_unum_param(&builder, "g", api_params->game_ids[0]);
|
|
423
|
+
|
|
424
|
+
for (i = 1; i < api_params->num_game_ids; i++) {
|
|
425
|
+
int chars = snprintf(num, sizeof(num), "%u", api_params->game_ids[i]);
|
|
426
|
+
rc_url_builder_append(&builder, ",", 1);
|
|
427
|
+
rc_url_builder_append(&builder, num, chars);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
request->post_data = rc_url_builder_finalize(&builder);
|
|
431
|
+
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
|
432
|
+
|
|
433
|
+
return builder.result;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_response_t* response, const rc_api_server_response_t* server_response) {
|
|
437
|
+
rc_api_game_title_entry_t* entry;
|
|
438
|
+
rc_json_iterator_t iterator;
|
|
439
|
+
rc_json_field_t array_field;
|
|
440
|
+
int result;
|
|
441
|
+
|
|
442
|
+
rc_json_field_t fields[] = {
|
|
443
|
+
RC_JSON_NEW_FIELD("Success"),
|
|
444
|
+
RC_JSON_NEW_FIELD("Error"),
|
|
445
|
+
RC_JSON_NEW_FIELD("Response")
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
rc_json_field_t entry_fields[] = {
|
|
449
|
+
RC_JSON_NEW_FIELD("ID"),
|
|
450
|
+
RC_JSON_NEW_FIELD("Title"),
|
|
451
|
+
RC_JSON_NEW_FIELD("ImageIcon"),
|
|
452
|
+
RC_JSON_NEW_FIELD("ImageUrl")
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
memset(response, 0, sizeof(*response));
|
|
456
|
+
rc_buffer_init(&response->response.buffer);
|
|
457
|
+
|
|
458
|
+
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
|
459
|
+
if (result != RC_OK)
|
|
460
|
+
return result;
|
|
461
|
+
|
|
462
|
+
if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &fields[2], "Response"))
|
|
463
|
+
return RC_MISSING_VALUE;
|
|
464
|
+
|
|
465
|
+
if (response->num_entries) {
|
|
466
|
+
response->entries = (rc_api_game_title_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_title_entry_t));
|
|
467
|
+
if (!response->entries)
|
|
468
|
+
return RC_OUT_OF_MEMORY;
|
|
469
|
+
|
|
470
|
+
memset(&iterator, 0, sizeof(iterator));
|
|
471
|
+
iterator.json = array_field.value_start;
|
|
472
|
+
iterator.end = array_field.value_end;
|
|
473
|
+
|
|
474
|
+
entry = response->entries;
|
|
475
|
+
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
|
|
476
|
+
if (!rc_json_get_required_unum(&entry->id, &response->response, &entry_fields[0], "ID"))
|
|
477
|
+
return RC_MISSING_VALUE;
|
|
478
|
+
if (!rc_json_get_required_string(&entry->title, &response->response, &entry_fields[1], "Title"))
|
|
479
|
+
return RC_MISSING_VALUE;
|
|
480
|
+
|
|
481
|
+
/* ImageIcon will be '/Images/0123456.png' - only return the '0123456' */
|
|
482
|
+
rc_json_extract_filename(&entry_fields[2]);
|
|
483
|
+
if (!rc_json_get_required_string(&entry->image_name, &response->response, &entry_fields[2], "ImageIcon"))
|
|
484
|
+
return RC_MISSING_VALUE;
|
|
485
|
+
|
|
486
|
+
rc_json_get_optional_string(&entry->image_url, &response->response, &entry_fields[3], "ImageUrl", "");
|
|
487
|
+
if (!entry->image_url[0])
|
|
488
|
+
entry->image_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_GAME, entry->image_name);
|
|
489
|
+
|
|
490
|
+
++entry;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return RC_OK;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
void rc_api_destroy_fetch_game_titles_response(rc_api_fetch_game_titles_response_t* response) {
|
|
498
|
+
rc_buffer_destroy(&response->response.buffer);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* --- Fetch Game Hashes --- */
|
|
502
|
+
|
|
503
|
+
int rc_api_init_fetch_hash_library_request(rc_api_request_t* request,
|
|
504
|
+
const rc_api_fetch_hash_library_request_t* api_params)
|
|
505
|
+
{
|
|
506
|
+
return rc_api_init_fetch_hash_library_request_hosted(request, api_params, &g_host);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
int rc_api_init_fetch_hash_library_request_hosted(rc_api_request_t* request,
|
|
510
|
+
const rc_api_fetch_hash_library_request_t* api_params,
|
|
511
|
+
const rc_api_host_t* host)
|
|
512
|
+
{
|
|
513
|
+
rc_api_url_builder_t builder;
|
|
514
|
+
rc_api_url_build_dorequest_url(request, host);
|
|
515
|
+
|
|
516
|
+
/* note: unauthenticated request */
|
|
517
|
+
rc_url_builder_init(&builder, &request->buffer, 48);
|
|
518
|
+
rc_url_builder_append_str_param(&builder, "r", "hashlibrary");
|
|
519
|
+
if (api_params->console_id != 0)
|
|
520
|
+
rc_url_builder_append_unum_param(&builder, "c", api_params->console_id);
|
|
521
|
+
|
|
522
|
+
request->post_data = rc_url_builder_finalize(&builder);
|
|
523
|
+
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
|
524
|
+
|
|
525
|
+
return builder.result;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
int rc_api_process_fetch_hash_library_server_response(rc_api_fetch_hash_library_response_t* response,
|
|
529
|
+
const rc_api_server_response_t* server_response)
|
|
530
|
+
{
|
|
531
|
+
rc_api_hash_library_entry_t* entry;
|
|
532
|
+
rc_json_iterator_t iterator;
|
|
533
|
+
rc_json_field_t field;
|
|
534
|
+
int result;
|
|
535
|
+
|
|
536
|
+
rc_json_field_t fields[] = {
|
|
537
|
+
RC_JSON_NEW_FIELD("Success"),
|
|
538
|
+
RC_JSON_NEW_FIELD("Error"),
|
|
539
|
+
RC_JSON_NEW_FIELD("MD5List"),
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
memset(response, 0, sizeof(*response));
|
|
543
|
+
rc_buffer_init(&response->response.buffer);
|
|
544
|
+
|
|
545
|
+
result =
|
|
546
|
+
rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
|
547
|
+
if (result != RC_OK)
|
|
548
|
+
return result;
|
|
549
|
+
|
|
550
|
+
if (!fields[2].value_start) {
|
|
551
|
+
/* call rc_json_get_required_object to generate the error message */
|
|
552
|
+
rc_json_get_required_object(NULL, 0, &response->response, &fields[2], "MD5List");
|
|
553
|
+
return RC_MISSING_VALUE;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
response->num_entries = fields[2].array_size;
|
|
557
|
+
if (response->num_entries > 0) {
|
|
558
|
+
rc_buffer_reserve(&response->response.buffer, response->num_entries * (33 + sizeof(rc_api_hash_library_entry_t)));
|
|
559
|
+
|
|
560
|
+
response->entries = (rc_api_hash_library_entry_t*)rc_buffer_alloc(
|
|
561
|
+
&response->response.buffer, response->num_entries * sizeof(rc_api_hash_library_entry_t));
|
|
562
|
+
if (!response->entries)
|
|
563
|
+
return RC_OUT_OF_MEMORY;
|
|
564
|
+
|
|
565
|
+
memset(&iterator, 0, sizeof(iterator));
|
|
566
|
+
iterator.json = fields[2].value_start;
|
|
567
|
+
iterator.end = fields[2].value_end;
|
|
568
|
+
|
|
569
|
+
entry = response->entries;
|
|
570
|
+
while (rc_json_get_next_object_field(&iterator, &field)) {
|
|
571
|
+
entry->hash = rc_buffer_strncpy(&response->response.buffer, field.name, field.name_len);
|
|
572
|
+
|
|
573
|
+
field.name = "";
|
|
574
|
+
if (!rc_json_get_unum(&entry->game_id, &field, ""))
|
|
575
|
+
return RC_MISSING_VALUE;
|
|
576
|
+
|
|
577
|
+
++entry;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return RC_OK;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
void rc_api_destroy_fetch_hash_library_response(rc_api_fetch_hash_library_response_t* response)
|
|
585
|
+
{
|
|
586
|
+
rc_buffer_destroy(&response->response.buffer);
|
|
587
|
+
}
|