update helpers/location to 'frosted' ui
[carveJwlIkooP6JGAAIwe30JlM.git] / steam.c
1 #include "vg/vg_steam.h"
2 #include "vg/vg_steam_utils.h"
3 #include "vg/vg_steam_networking.h"
4 #include "vg/vg_steam_auth.h"
5 #include "vg/vg_steam_http.h"
6 #include "vg/vg_steam_friends.h"
7 #include "vg/vg_steam_user_stats.h"
8 #include "submodules/anyascii/impl/c/anyascii.c"
9 #include "skaterift.h"
10 #include <string.h>
11
12 /*
13 * We only want to use steamworks if building for the networked version,
14 * theres not much point otherwise. We mainly want steamworks for setting
15 * achievements etc.. so that includes our own server too.
16 *
17 * This file also wraps the functions and interfaces that we want to use to
18 * make them a bit easier to read, since they are the flat API they have very
19 * long names. in non-networked builds they will return default errors or do
20 * nothing.
21 */
22
23 char steam_username_at_startup[128] = "Unassigned";
24
25 static void recv_steam_warning( int severity, const char *msg )
26 {
27 if( severity == 0 )
28 vg_low( "%s\n", msg );
29 else
30 vg_info( "%s\n", msg );
31 }
32
33 int steam_ready = 0,
34 steam_stats_ready = 0;
35
36 void *hSteamNetworkingSockets, *hSteamUser, *hSteamUserStats;
37 static HSteamPipe hSteamClientPipe;
38
39 static const char *steam_achievement_names[] =
40 {
41 "ALBERT", "MARC", "JANET", "BERNADETTA",
42 "ROUTE_MPY", "ROUTE_MPG", "ROUTE_MPB", "ROUTE_MPR",
43 "ROUTE_TO", "ROUTE_TC", "CITY_COMPLETE", "MTZERO_SILVER", "MTZERO_GOLD",
44 "80FT"
45 };
46
47 void steam_store_achievements(void)
48 {
49 if( steam_ready && steam_stats_ready ){
50 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats );
51 }
52 }
53
54 void update_ach_models(void);
55 void steam_set_achievement( const char *name )
56 {
57 if( skaterift.demo_mode )
58 return;
59
60 /* hack lol */
61 if( !strcmp(name,"MARC") ) skaterift.achievements |= 0x1;
62 if( !strcmp(name,"ALBERT") ) skaterift.achievements |= 0x2;
63 if( !strcmp(name,"JANET") ) skaterift.achievements |= 0x4;
64 if( !strcmp(name,"BERNADETTA") ) skaterift.achievements |= 0x8;
65 update_ach_models();
66
67 if( steam_ready && steam_stats_ready ){
68 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats, name ) ){
69 vg_success( "Achievement set! '%s'\n", name );
70
71 }
72 else{
73 vg_warn( "Failed to set achievement: %s\n", name );
74 }
75 }
76 else{
77 vg_warn( "Failed to set achievement (steam not ready): %s\n", name );
78 }
79 }
80
81 void steam_clear_achievement( const char *name )
82 {
83 if( steam_ready && steam_stats_ready ){
84 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats, name ) ){
85 vg_info( "Achievement cleared: '%s'\n", name );
86 }
87 else{
88 vg_warn( "Failed to clear achievement: %s\n", name );
89 }
90 }
91 else{
92 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name );
93 }
94 }
95
96
97 void steam_print_all_achievements(void)
98 {
99 vg_info( "Achievements: \n" );
100
101 if( steam_ready && steam_stats_ready ){
102 for( int i=0; i<vg_list_size(steam_achievement_names); i++ ){
103 steamapi_bool set = 0;
104 const char *name = steam_achievement_names[i];
105
106 if( SteamAPI_ISteamUserStats_GetAchievement(
107 hSteamUserStats, name, &set ) )
108 {
109 vg_info( " %s %s\n", (set? "[YES]": "[ ]"), name );
110 }
111 else{
112 vg_warn( " Error while fetching achievement status '%s'\n", name );
113 }
114 }
115 }
116 else{
117 vg_warn( " Steam is not initialized, no results\n" );
118 }
119 }
120
121 int steam_achievement_ccmd( int argc, char const *argv[] )
122 {
123 if( !(steam_ready && steam_stats_ready) ) return 1;
124
125 if( argc == 1 ){
126 if( !strcmp( argv[0], "list" ) ){
127 steam_print_all_achievements();
128 return 0;
129 }
130 else if( !strcmp( argv[0], "clearall" )){
131 for( int i=0; i<vg_list_size(steam_achievement_names); i++ )
132 steam_clear_achievement( steam_achievement_names[i] );
133
134 steam_store_achievements();
135 }
136 }
137
138 if( argc == 2 ){
139 if( !strcmp( argv[0], "set" ) ){
140 steam_set_achievement( argv[1] );
141 steam_store_achievements();
142 return 0;
143 }
144 else if( strcmp( argv[0], "clear" ) ){
145 steam_clear_achievement( argv[1] );
146 steam_store_achievements();
147 return 0;
148 }
149 }
150
151 return 1;
152 }
153
154 static void steam_on_recieve_current_stats( CallbackMsg_t *msg )
155 {
156 UserStatsReceived_t *rec = (UserStatsReceived_t *)msg->m_pubParam;
157
158 if( rec->m_eResult == k_EResultOK ){
159 vg_info( "Recieved stats for: %lu (user: %lu)\n", rec->m_nGameID,
160 rec->m_steamIDUser );
161 steam_stats_ready = 1;
162
163 steamapi_bool set = 0;
164 if( SteamAPI_ISteamUserStats_GetAchievement(
165 hSteamUserStats, "MARC", &set ) ){
166 if( set ) skaterift.achievements |= 0x1;
167 }
168 if( SteamAPI_ISteamUserStats_GetAchievement(
169 hSteamUserStats, "ALBERT", &set ) ){
170 if( set ) skaterift.achievements |= 0x2;
171 }
172 if( SteamAPI_ISteamUserStats_GetAchievement(
173 hSteamUserStats, "JANET", &set ) ){
174 if( set ) skaterift.achievements |= 0x4;
175 }
176 if( SteamAPI_ISteamUserStats_GetAchievement(
177 hSteamUserStats, "BERNADETTA", &set ) ){
178 if( set ) skaterift.achievements |= 0x8;
179 }
180 update_ach_models();
181 }
182 else{
183 vg_error( "Error recieveing stats for user (%u)\n", rec->m_eResult );
184 }
185 }
186
187 static u32 utf8_byte0_byte_count( u8 char0 )
188 {
189 for( u32 k=2; k<4; k++ ){
190 if( !(char0 & (0x80 >> k)) )
191 return k;
192 }
193
194 return 0;
195 }
196
197 u32 str_utf8_collapse( const char *str, char *buf, u32 length )
198 {
199 u8 *ustr = (u8 *)str;
200 u32 utf32_code = 0x00000000;
201 u32 i=0, j=0, utf32_byte_ct=0;
202
203 for(;j < length-1;){
204 if( ustr[i] == 0x00 )
205 break;
206
207 if( ustr[i] & 0x80 ){
208 if( utf32_byte_ct ){
209 utf32_byte_ct --;
210 utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6);
211
212 if( !utf32_byte_ct ){
213 const char *match;
214 size_t chars = anyascii( utf32_code, &match );
215
216 for( u32 k=0; k<VG_MIN(chars, length-1-j); k++ ){
217 buf[ j++ ] = (u8)match[k];
218 }
219 }
220 }
221 else{
222 utf32_byte_ct = utf8_byte0_byte_count( ustr[i] )-1;
223 utf32_code = ustr[i] & (0x3F >> utf32_byte_ct);
224 utf32_code <<= utf32_byte_ct*6;
225 }
226 }
227 else{
228 utf32_byte_ct = 0x00;
229 buf[j ++] = str[i];
230 }
231
232 i++;
233 }
234
235 buf[j] = 0x00;
236 return j;
237 }
238
239 int steam_init(void)
240 {
241 const char *username = "offline player";
242
243 vg_info( "Initializing steamworks\n" );
244
245 if( !SteamAPI_Init() ){
246 printf("\n");
247 vg_error( "Steamworks failed to initialize\n" );
248 return 1;
249 }
250
251 steam_ready = 1;
252
253 SteamAPI_ManualDispatch_Init();
254
255 /* Connect interfaces */
256 hSteamClientPipe = SteamAPI_GetHSteamPipe();
257 hSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI();
258 hSteamUser = SteamAPI_SteamUser();
259
260 ISteamUtils *utils = SteamAPI_SteamUtils();
261 SteamAPI_ISteamUtils_SetWarningMessageHook( utils, recv_steam_warning );
262
263 printf("\n");
264 vg_success( "\nSteamworks API running\n" );
265
266 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
267 username = SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends );
268
269 /*
270 * Request stats
271 * --------------------------------------------------------
272 */
273 hSteamUserStats = SteamAPI_SteamUserStats();
274 steam_register_callback( k_iUserStatsReceived,
275 steam_on_recieve_current_stats );
276
277 if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats ) )
278 vg_warn( "No Steam Logon: Cannot request stats\n" );
279
280
281 vg_console_reg_cmd( "ach", steam_achievement_ccmd, NULL );
282
283 /* TODO: On username update callback */
284 str_utf8_collapse( username, steam_username_at_startup,
285 vg_list_size(steam_username_at_startup) );
286
287 return 1;
288 }
289
290 void steam_update(void)
291 {
292 if( steam_ready ){
293 steamworks_event_loop( hSteamClientPipe );
294 }
295 }
296
297 void steam_end(void)
298 {
299 if( steam_ready ){
300 vg_info( "Shutting down\n..." );
301 SteamAPI_Shutdown();
302 }
303 }