diff --git a/Makefile.common b/Makefile.common index 17d88b8772..18f685e5c5 100644 --- a/Makefile.common +++ b/Makefile.common @@ -815,7 +815,7 @@ endif # Retro Achievements -OBJ += cheevos.o +OBJ += cheevos.o libretro-common/utils/md5.o ifeq ($(HAVE_CHEEVOS), 1) ifeq ($(HAVE_SMW_CHEEVOS), 1) diff --git a/cheevos.c b/cheevos.c index 2ca93f4d5c..28ab1d2af7 100644 --- a/cheevos.c +++ b/cheevos.c @@ -21,20 +21,13 @@ #include #include +#include +#include #include #include "cheevos.h" #include "dynamic.h" -#if 0 - -curl -v "http://retroachievements.org/dorequest.php?r=login&u=leiradel&p=####" -curl -v "http://retroachievements.org/dorequest.php?r=gameid&u=leiradel&m=c320cba1be26f303ba6f60d39ec39855" -curl -v "http://retroachievements.org/dorequest.php?r=patch&u=leiradel&g=228&f=3&l=1&t=9YtR63TDb2BB0vlN" -curl "http://i.retroachievements.org/Badge/05506.png" - -#endif - enum { CHEEVOS_VAR_SIZE_BIT_0, @@ -160,6 +153,10 @@ cheevos_config_t cheevos_config = { /* enable */ 1, /* test_unofficial */ 0, + /* username */ "libretro", + /* password */ "l1br3tro3456", + /* token */ { 0 }, + /* game_id */ 0, }; #ifdef HAVE_CHEEVOS @@ -167,63 +164,6 @@ cheevos_config_t cheevos_config = static cheevoset_t core_cheevos = { NULL, 0 }; static cheevoset_t unofficial_cheevos = { NULL, 0 }; -#ifdef HAVE_SMW_CHEEVOS - -/***************************************************************************** -Achievements for SNES' Super Mario World. -*****************************************************************************/ - -static const char* cheevos_smw_json = - "{\"Success\":true,\"PatchData\":{\"ID\":228,\"Title\":\"Super Mario World\",\"ConsoleID\":3,\"ForumTopicID\":135,\"Flags\":0,\"ImageIcon\":\"/Images/000284.png\",\"ImageTitle\":\"/Images/000021.png\",\"ImageIngame\":\"/Images/000022.png\",\"ImageBoxArt\":\"/Images/000138.png\",\"Publisher\":\"Nintendo\",\"Developer\":\"Nintendo EAD\",\"Genre\":\"Platforming\",\"Released\":\"JP 1990 , NA 1991 Europe 1992\",\"IsFinal\":false,\"ConsoleName\":\"SNES\",\"RichPresencePatch\":\"Lookup:LevelName\r\n0x0=Title Screen\r\n0x14=Yellow Switch Palace\r\n0x28=Yoshi's House\r\n0x29=Yoshi's Island 1\r\n0x2a=Yoshi's Island 2\r\n0x27=Yoshi's Island 3\r\n0x26=Yoshi's Island 4\r\n0x25=#1 Iggy's Castle\r\n0x15=Donut Plains 1\r\n0x9=Donut Plains 2\r\n0x8=Green Switch Palace\r\n0x4=Donut Ghost House\r\n0x3=Top Secret Area\r\n0x5=Donut Plains 3\r\n0x6=Donut Plains 4\r\n0x7=#2 Morton's Castle\r\n0xa=Donut Secret 1\r\n0x13=Donut Secret House\r\n0x2f=Donut Secret 2\r\n0x3e=Vanilla Dome 1\r\n0x3c=Vanilla Dome 2\r\n0x3f=Red Switch Palace\r\n0x2b=Vanilla Ghost House\r\n0x2e=Vanilla Dome 3\r\n0x3d=Vanilla Dome 4\r\n0x40=#3 Lemmy's Castle\r\n0x2d=Vanilla Secret 1\r\n0x1=Vanilla Secret 2\r\n0x2=Vanilla Secret 3\r\n0xb=Vanilla Fortress\r\n0xc=Butter Bridge 1\r\n0xd=Butter Bridge 2\r\n0xe=#4 Ludwig's Castle\r\n0xf=Cheese Bridge Area\r\n0x10=Cookie Mountain\r\n0x11=Soda Lake\r\n0x41=Forest Ghost House\r\n0x42=Forest of Illusion 1\r\n0x43=Forest of Illusion 4\r\n0x44=Forest of Illusion 2\r\n0x45=Blue Switch Palace\r\n0x46=Forest Secret Area\r\n0x47=Forest of Illusion 3\r\n0x1f=Forest Fortress\r\n0x20=#5 Roy's Castle\r\n0x21=Choco-Ghost House\r\n0x22=Chocolate Island 1\r\n0x23=Chocolate Island 3\r\n0x24=Chocolate Island 2\r\n0x1b=Chocolate Fortress\r\n0x1d=Chocolate Island 4\r\n0x1c=Chocolate Island 5\r\n0x1a=#6 Wendy's Castle\r\n0x18=Sunken Ghost Ship\r\n0x3b=Chocolate Secret\r\n0x3a=Valley of Bowser 1\r\n0x39=Valley of Bowser 2\r\n0x38=Valley Ghost House\r\n0x37=Valley of Bowser 3\r\n0x33=Valley of Bowser 4\r\n0x34=#7 Larry's Castle\r\n0x35=Valley Fortress\r\n0x31=Front Door\r\n0x32=Back Door\r\n0x58=Star World 1\r\n0x54=Star World 2\r\n0x56=Star World 3\r\n0x59=Star World 4\r\n0x5a=Star World 5\r\n0x4e=Gnarly\r\n0x4f=Tubular\r\n0x50=Way Cool\r\n0x51=Awesome\r\n0x4c=Groovy\r\n0x4b=Mondo\r\n0x4a=Outrageous\r\n0x49=Funky\r\n\r\nFormat:Lives\r\nFormatType=VALUE\r\n\r\nDisplay:\r\n@LevelName(0xh0013bf), @Lives(0xh0dbe_v+1) live(s)\",\"Achievements\":[" - "{\"ID\":4874,\"MemAddr\":\"0xH000019=2\",\"Title\":\"I Believe I Can Fly\",\"Description\":\"Collect a feather\",\"Points\":5,\"Author\":\"UNHchabo\",\"Modified\":1434153343,\"Created\":1391908064,\"BadgeName\":\"05506\",\"Flags\":3}," - "{\"ID\":4933,\"MemAddr\":\"0xH0018c2>0\",\"Title\":\"Floating Through the Clouds\",\"Description\":\"Hijack a Lakitu cloud\",\"Points\":10,\"Author\":\"UNHchabo\",\"Modified\":1441035935,\"Created\":1392010935,\"BadgeName\":\"00085\",\"Flags\":5}," - "{\"ID\":4934,\"MemAddr\":\"0xH0013c7=4\",\"Title\":\"Yellow Yoshi\",\"Description\":\"Ride a yellow Yoshi\",\"Points\":10,\"Author\":\"UNHchabo\",\"Modified\":1410734527,\"Created\":1392011140,\"BadgeName\":\"00085\",\"Flags\":5}," - "{\"ID\":4935,\"MemAddr\":\"0xH0013c7=6\",\"Title\":\"Blue Yoshi\",\"Description\":\"Ride a blue Yoshi\",\"Points\":10,\"Author\":\"UNHchabo\",\"Modified\":1410735517,\"Created\":1392011155,\"BadgeName\":\"00085\",\"Flags\":5}," - "{\"ID\":4936,\"MemAddr\":\"0xH0013c7=8\",\"Title\":\"Red Yoshi\",\"Description\":\"Ride a red Yoshi\",\"Points\":10,\"Author\":\"UNHchabo\",\"Modified\":1410735526,\"Created\":1392011190,\"BadgeName\":\"00085\",\"Flags\":5}," - "{\"ID\":4937,\"MemAddr\":\"0xH000dbe>=98_0xH000019=1\",\"Title\":\"Mushroom Collector\",\"Description\":\"Get 99 lives, and become Super Mario\",\"Points\":10,\"Author\":\"UNHchabo\",\"Modified\":1392018537,\"Created\":1392018537,\"BadgeName\":\"00085\",\"Flags\":5}," - "{\"ID\":5756,\"MemAddr\":\"0xH0013c5>0\",\"Title\":\"Shoot the Moon!\",\"Description\":\"Collect a 3-Up\",\"Points\":10,\"Author\":\"UNHchabo\",\"Modified\":1393710657,\"Created\":1393710657,\"BadgeName\":\"05505\",\"Flags\":5}," - "{\"ID\":341,\"MemAddr\":\"0x1420=1\",\"Title\":\"Unleash the Dragon\",\"Description\":\"Collect 5 Dragon Coins in a level\",\"Points\":10,\"Author\":\"Scott\",\"Modified\":1367266589,\"Created\":1367266583,\"BadgeName\":\"00549\",\"Flags\":3}," - "{\"ID\":342,\"MemAddr\":\"0xH000dc1=1_0xH0013bf>0\",\"Title\":\"Giddy Up!\",\"Description\":\"Catch a ride with a friend\",\"Points\":10,\"Author\":\"Scott\",\"Modified\":1376974455,\"Created\":1367266931,\"BadgeName\":\"00550\",\"Flags\":3}," - "{\"ID\":340,\"MemAddr\":\"0xH0dbf=99\",\"Title\":\"Rich Mario\",\"Description\":\"Collect 99 coins!\",\"Points\":10,\"Author\":\"Scott\",\"Modified\":1367254980,\"Created\":1367254976,\"BadgeName\":\"00547\",\"Flags\":3}," - "{\"ID\":1706,\"MemAddr\":\"0xH001900=80\",\"Title\":\"Maximum Finish\",\"Description\":\"Cross the finish line at the end of the stage and collect the max 50 stars\",\"Points\":10,\"Author\":\"jackolantern\",\"Modified\":1372762549,\"Created\":1372674230,\"BadgeName\":\"02014\",\"Flags\":3}," - "{\"ID\":2246,\"MemAddr\":\"0xH000f31=0(20)_R:0xH000f31!=0_0xH000f32=0(20)_R:0xH000f32!=0_0xH000f33=0(20)_R:0xH000f33!=0_0xH000dbe>d0xH000dbe(8)_0xH001411=1(20)_R:0xH001411=0\",\"Title\":\"Perfect Bonus Stage\",\"Description\":\"Score 8 Extra Lives on the Bonus Game\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376611582,\"Created\":1376582613,\"BadgeName\":\"02739\",\"Flags\":3}," - "{\"ID\":2199,\"MemAddr\":\"0xH001f28=1_0xH001f27=1_0xH001f29=1_0xH001f2a=1\",\"Title\":\"Filling all the blocks in\",\"Description\":\"Hit the buttons in all four coloured switch palaces.\",\"Points\":20,\"Author\":\"jackolantern\",\"Modified\":1376552794,\"Created\":1376514114,\"BadgeName\":\"02720\",\"Flags\":3}," - "{\"ID\":2253,\"MemAddr\":\"0xH0013bf=37_0xH000dd5=1\",\"Title\":\"Iggy Koopa\",\"Description\":\"Defeat Iggy Koopa of Castle #1\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1425959554,\"Created\":1376616356,\"BadgeName\":\"00562\",\"Flags\":3}," - "{\"ID\":347,\"MemAddr\":\"0xH0013bf=7_0xH000dd5=1\",\"Title\":\"Morton Koopa Jr\",\"Description\":\"Defeat Morton Koopa Jr of Castle #2\",\"Points\":10,\"Author\":\"Scott\",\"Modified\":1425959561,\"Created\":1367322700,\"BadgeName\":\"00562\",\"Flags\":3}," - "{\"ID\":2261,\"MemAddr\":\"0xH0013bf=64_0xH000dd5=1\",\"Title\":\"Lemmy Koopa\",\"Description\":\"Defeat Lemmy Koopa of Castle #3\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1425959153,\"Created\":1376652522,\"BadgeName\":\"00562\",\"Flags\":3}," - "{\"ID\":2262,\"MemAddr\":\"0xH0013bf=14_0xH000dd5=1\",\"Title\":\"Ludwig von Koopa\",\"Description\":\"Defeat Ludwig von Koopa of Castle #4\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1425959133,\"Created\":1376653163,\"BadgeName\":\"00562\",\"Flags\":3}," - "{\"ID\":2306,\"MemAddr\":\"0xH0013bf=32_0xH000dd5=1\",\"Title\":\"Roy Koopa\",\"Description\":\"Defeat Roy Koopa of Castle #5\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1425956637,\"Created\":1376938808,\"BadgeName\":\"00562\",\"Flags\":3}," - "{\"ID\":2309,\"MemAddr\":\"0xH0013bf=26_0xH000906=1.400._R:0xH0013bf!=26\",\"Title\":\"Wendy O. Koopa\",\"Description\":\"Defeat Wendy O. Koopa of Castle #6\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1390938088,\"Created\":1376939582,\"BadgeName\":\"00562\",\"Flags\":3}," - "{\"ID\":2342,\"MemAddr\":\"0xH0013bf=52_0xH000dd5=1\",\"Title\":\"Larry Koopa\",\"Description\":\"Defeat Larry Koopa of Castle #7\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1425959095,\"Created\":1376970283,\"BadgeName\":\"00562\",\"Flags\":3}," - "{\"ID\":2250,\"MemAddr\":\"0xH0013bf=11(20)_0xH000906=1(20)_R:0xH001411!=1\",\"Title\":\"Reznor\",\"Description\":\"Defeat the Reznor atop Vanilla Dome\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376615087,\"Created\":1376615073,\"BadgeName\":\"02742\",\"Flags\":3}," - "{\"ID\":2307,\"MemAddr\":\"0xH0013bf=31(20)_0xH000906=1(20)_R:0xH001411!=1\",\"Title\":\"Reznor Again?\",\"Description\":\"Defeat the Reznor in the clearing of the Forest of Illusion\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376938847,\"Created\":1376938811,\"BadgeName\":\"02742\",\"Flags\":3}," - "{\"ID\":2308,\"MemAddr\":\"0xH0013bf=27(20)_0xH000906=1(20)_R:0xH001411!=1\",\"Title\":\"Reznor, do you ever give up?\",\"Description\":\"Defeat the Reznor at the center of Chocolate Island\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376938850,\"Created\":1376938815,\"BadgeName\":\"02742\",\"Flags\":3}," - "{\"ID\":2338,\"MemAddr\":\"0xH0013bf=53(20)_0xH000906=1(20)_R:0xH001411!=1\",\"Title\":\"Reznor...\",\"Description\":\"Defeat the Reznor in Bowsers Valley\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376969439,\"Created\":1376969412,\"BadgeName\":\"02742\",\"Flags\":3}," - "{\"ID\":2275,\"MemAddr\":\"0xH0013bf=49_R:0xH0013bf!=49_0xH0013f9=3_R:0xH0013f9!=3_0xH0013ef=1_R:0xH0013ef!=1\",\"Title\":\"King Bowser Koopa\",\"Description\":\"Beat Bowser and Save the Princess (Front Door!)\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1390856575,\"Created\":1376742802,\"BadgeName\":\"02764\",\"Flags\":3}," - "{\"ID\":2276,\"MemAddr\":\"0xH0013bf=49_R:0xH0013bf!=49_0xH0013f9=3_R:0xH0013f9!=3_0xH0013ef=1_R:0xH0013ef!=1_0xH000019=0_R:0xH000019!=0\",\"Title\":\"Baby's First Kiss\",\"Description\":\"Get the Princess Kiss as Little Mario (Front Door!)\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1390856597,\"Created\":1376742805,\"BadgeName\":\"02765\",\"Flags\":3}," - "{\"ID\":2277,\"MemAddr\":\"0xH0013bf=49_R:0xH0013bf!=49_0xH0013f9=3_R:0xH0013f9!=3_0xH0013ef=1_R:0xH0013ef!=1_0xH000019=3_R:0xH000019!=3\",\"Title\":\"Burning Bowser\",\"Description\":\"Get the Princess Kiss as Fire Mario (Front Door!)\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1390856617,\"Created\":1376742808,\"BadgeName\":\"02766\",\"Flags\":3}," - "{\"ID\":2278,\"MemAddr\":\"0xH0013bf=49_R:0xH0013bf!=49_0xH0013f9=3_R:0xH0013f9!=3_0xH0013ef=1_R:0xH0013ef!=1_0xH000019=2_R:0xH000019!=2\",\"Title\":\"Flying Finish\",\"Description\":\"Get the Princess Kiss as Cape Mario (Front Door!)\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1390856673,\"Created\":1376742811,\"BadgeName\":\"02767\",\"Flags\":3}," - "{\"ID\":2299,\"MemAddr\":\"0xH000020=168_R:0xH000020!=168_0xH00001e=248_R:0xH00001e!=248_0xH0013c3=0_R:0xH0013c3!=0_0xH0013bf=19_R:0xH0013bf!=19_0xH001411=0\",\"Title\":\"The Big Boo\",\"Description\":\"Defeat the Big Boo in Donut Secret House\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1378767596,\"Created\":1376918022,\"BadgeName\":\"02797\",\"Flags\":3}," - "{\"ID\":2298,\"MemAddr\":\"0xH0013c3=6\",\"Title\":\"To the Stars!\",\"Description\":\"Reach the Star Road\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376918072,\"Created\":1376918019,\"BadgeName\":\"02798\",\"Flags\":3}," - "{\"ID\":2264,\"MemAddr\":\"0xH000007=114_R:0xH000007!=114_0xH000008=121_R:0xH000008!=121_0xH0013c3=3_R:0xH0013c3!=3_0xH001411=0\",\"Title\":\"I could've sworn...\",\"Description\":\"Get lost in the Forest of Illusion\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376941372,\"Created\":1376657732,\"BadgeName\":\"02753\",\"Flags\":3}," - "{\"ID\":2305,\"MemAddr\":\"0xH00001e=169_0xH000020=28_0xH0013c3=0_0xH001411=0\",\"Title\":\"Chocolate Donut\",\"Description\":\"Walk in a circle on Chocolate Island\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376945561,\"Created\":1376938805,\"BadgeName\":\"02806\",\"Flags\":3}," - "{\"ID\":2300,\"MemAddr\":\"0xH0013c3=5\",\"Title\":\"Mario's Special Place\",\"Description\":\"Get to the challenging Special Stages\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376918077,\"Created\":1376918026,\"BadgeName\":\"02800\",\"Flags\":3}," - "{\"ID\":2302,\"MemAddr\":\"0xH0013c3=5(1)_0xH0013c3=1(1)_R:0xH0013c3=6_R:0xH0013c3=0_R:0xH001f79=0\",\"Title\":\"Change of Scenery\",\"Description\":\"Clear the Special Zone and change the seasons in Mushroom Kingdom\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376929315,\"Created\":1376929303,\"BadgeName\":\"02803\",\"Flags\":3}," - "{\"ID\":2252,\"MemAddr\":\"0xH0013f3=1(20)_0xH001411=1(20)_R:0xH001411=0_0xH000019=0(20)_R:0xH000019!=0(20)\",\"Title\":\"Too Much Pasta\",\"Description\":\"Let Little Mario load up on mama Luigi's Famous P(asta)-gas!\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376651934,\"Created\":1376615082,\"BadgeName\":\"02744\",\"Flags\":3}," - "{\"ID\":2251,\"MemAddr\":\"0xH0013f3=1(20)_0xH001411=1(20)_R:0xH001411=0_0xH000019!=0_R:0xH000019=0\",\"Title\":\"Another kind of flying\",\"Description\":\"Send Mario flying with P-gas\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376617016,\"Created\":1376615078,\"BadgeName\":\"02743\",\"Flags\":3}," - "{\"ID\":2263,\"MemAddr\":\"0xH001697=12_0xH0013bf=66_R:0xH001697=0\",\"Title\":\"Bother the Wigglers\",\"Description\":\"Jump on yellow Wigglers in Forest of Illusion 12 times in a row for a surprise!\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1379674609,\"Created\":1376655155,\"BadgeName\":\"02752\",\"Flags\":3}," - "{\"ID\":2274,\"MemAddr\":\"0xH0013bf=49_R:0xH0013bf!=49_0xH0013f9=3_R:0xH0013f9!=3_0xH001f2e=11_R:0xH001f2e!=11_0xH0013ef=1_R:0xH0013ef!=1\",\"Title\":\"Shortest Route\",\"Description\":\"Clear the fewest stages possible and beat the game\",\"Points\":10,\"Author\":\"Jaarl\",\"Modified\":1376742834,\"Created\":1376742799,\"BadgeName\":\"02768\",\"Flags\":3}," - "{\"ID\":2297,\"MemAddr\":\"0xH001f2e=0.1._0xH001f2e=1.1._0xH001f2e=2.1._0xH001f2e=3.1._0xH001f2e=4.1._0xH001f2e=5.1._0xH001f2e=6.1._0xH001f2e=7.1._0xH001f2e=8.1._0xH001f2e=9.1._0xH001f2e=10.1._0xH001f2e=11.1._0xH0013f9=3_R:0xH000dbeis_key = cheevos_djb2( name, length ) == ud->key_hash; + return 0; +} + +static int getvalue__json_string( void* userdata, const char* string, size_t length ) +{ + cheevo_getvalueud_t* ud = (cheevo_getvalueud_t*)userdata; + + if ( ud->is_key ) + { + ud->value = string; + ud->length = length; + ud->is_key = 0; + } + + return 0; +} + +static int getvalue__json_boolean( void* userdata, int istrue ) +{ + cheevo_getvalueud_t* ud = (cheevo_getvalueud_t*)userdata; + + if ( ud->is_key ) + { + ud->value = istrue ? "true" : "false"; + ud->length = istrue ? 4 : 5; + ud->is_key = 0; + } + + return 0; +} + +static int getvalue__json_null( void* userdata ) +{ + cheevo_getvalueud_t* ud = (cheevo_getvalueud_t*)userdata; + + if ( ud->is_key ) + { + ud->value = "null"; + ud->length = 4; + ud->is_key = 0; + } + + return 0; +} + +static int cheevos_get_value( const char* json, unsigned key_hash, char* value, size_t length ) +{ + static const jsonsax_handlers_t handlers = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + getvalue__json_key, + NULL, + getvalue__json_string, + getvalue__json_string, /* number */ + getvalue__json_boolean, + getvalue__json_null + }; + + cheevo_getvalueud_t ud; + + ud.key_hash = key_hash; + ud.is_key = 0; + ud.length = 0; + *value = 0; + + if ( jsonsax_parse( json, &handlers, (void*)&ud ) == JSONSAX_OK && ud.length < length ) + { + strncpy( value, ud.value, length ); + value[ ud.length ] = 0; + return 0; + } + + return -1; +} + +static int cheevos_login( void ) +{ + int res = 0; + char request[ 256 ]; + const char* json; + cheevo_getvalueud_t ud; + + if ( cheevos_config.token[ 0 ] == 0 ) + { + cheevos_config.token[ 0 ] = 0; + + snprintf( + request, sizeof( request ), + "http://retroachievements.org/dorequest.php?r=login&u=%s&p=%s", + cheevos_config.username, cheevos_config.password + ); + + request[ sizeof( request ) - 1 ] = 0; + + json = cheevos_http_get( request, NULL ); + + if ( !json ) + { + return -1; + } + + res = cheevos_get_value( json, 0x0e2dbd26U /* Token */, cheevos_config.token, sizeof( cheevos_config.token ) ); + } + + RARCH_LOG( "cheevos user token is %s\n", cheevos_config.token ); + return res; +} + +int cheevos_get_by_game_id( const char** json, unsigned game_id ) +{ + char request[ 256 ]; + + cheevos_login(); + + snprintf( + request, sizeof( request ), + "http://retroachievements.org/dorequest.php?r=patch&u=%s&g=%u&f=3&l=1&t=%s", + cheevos_config.username, game_id, cheevos_config.token + ); + + request[ sizeof( request ) - 1 ] = 0; + + *json = cheevos_http_get( request, NULL ); + + if ( !*json ) + { + return -1; + } + + return 0; +} + +#define CHEEVOS_EIGHT_MB ( 8 * 1024 * 1024 ) + +int cheevos_get_by_content( const char** json, const void* data, size_t size, unsigned flags ) +{ + MD5_CTX ctx; + char buffer[ 4096 ]; + size_t len; + unsigned char hash[ 16 ]; + char request[ 256 ]; + char game_id[ 16 ]; + int res; + + MD5_Init( &ctx ); + MD5_Update( &ctx, data, size ); + + if ( ( flags & CHEEVOS_FLAGS_IS_SNES ) && size < CHEEVOS_EIGHT_MB ) + { + size = CHEEVOS_EIGHT_MB - size; + memset( (void*)buffer, 0, sizeof( buffer ) ); + + while ( size ) + { + len = sizeof( buffer ); + + if ( len > size ) + { + len = size; + } + + MD5_Update( &ctx, (void*)buffer, len ); + size -= len; + } + } + + MD5_Final( hash, &ctx ); + + snprintf( + request, sizeof( request ), + "http://retroachievements.org/dorequest.php?r=gameid&u=%s&m=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + cheevos_config.username, + hash[ 0 ], hash[ 1 ], hash[ 2 ], hash[ 3 ], + hash[ 4 ], hash[ 5 ], hash[ 6 ], hash[ 7 ], + hash[ 8 ], hash[ 9 ], hash[ 10 ], hash[ 11 ], + hash[ 12 ], hash[ 13 ], hash[ 14 ], hash[ 15 ] + ); + + request[ sizeof( request ) - 1 ] = 0; + + *json = cheevos_http_get( request, NULL ); + + if ( !*json ) + { + return -1; + } + + res = cheevos_get_value( *json, 0xb4960eecU /* GameID */, game_id, sizeof( game_id ) ); + free( (void*)*json ); + + if ( !res ) + { + RARCH_LOG( "game id is %s\n", game_id ); + res = cheevos_get_by_game_id( json, strtoul( game_id, NULL, 10 ) ); + } + + return res; +} + #else /* HAVE_CHEEVOS */ int cheevos_load( const char* json ) @@ -1168,4 +1403,16 @@ void cheevos_unload( void ) { } +int cheevos_get_by_game_id( const char** json, unsigned game_id ) +{ + *json = "{}"; + return -1; +} + +int cheevos_get_by_content( const char** json, const void* data, size_t size, unsigned flags ) +{ + *json = "{}"; + return -1; +} + #endif /* HAVE_CHEEVOS */ diff --git a/cheevos.h b/cheevos.h index 14ebc61488..a8dfdc27f0 100644 --- a/cheevos.h +++ b/cheevos.h @@ -21,13 +21,25 @@ typedef struct { unsigned char enable; unsigned char test_unofficial; + const char* username; + const char* password; + /* These are used by the implementation, do not touch. */ + char token[ 20 ]; + unsigned game_id; } cheevos_config_t; +enum +{ + CHEEVOS_FLAGS_IS_SNES = 1 << 0, /* forces * mb padded with zeroes */ +}; + extern cheevos_config_t cheevos_config; int cheevos_load( const char* json ); void cheevos_test( void ); void cheevos_unload( void ); +int cheevos_get_by_game_id( const char** json, unsigned game_id ); +int cheevos_get_by_content( const char** json, const void* data, size_t size, unsigned flags ); #endif /* __RARCH_CHEEVOS_H */ diff --git a/content.c b/content.c index 2e937fe322..aff3bfab2a 100644 --- a/content.c +++ b/content.c @@ -1,7 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2015 - Daniel De Matteis - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -44,6 +44,7 @@ #include "movie.h" #include "patch.h" #include "system.h" +#include "cheevos.h" /** * read_content_file: @@ -77,7 +78,7 @@ static bool read_content_file(unsigned i, const char *path, void **buf, /* Attempt to apply a patch. */ if (!global->patch.block_patch) patch_content(&ret_buf, length); - + #ifdef HAVE_ZLIB global->content_crc = zlib_crc32_calculate(ret_buf, *length); @@ -164,7 +165,7 @@ bool save_state(const char *path) if (!data) return false; - RARCH_LOG("%s: %d %s.\n", + RARCH_LOG("%s: %d %s.\n", msg_hash_to_str(MSG_STATE_SIZE), (int)size, msg_hash_to_str(MSG_BYTES)); @@ -174,7 +175,7 @@ bool save_state(const char *path) ret = retro_write_file(path, data, size); if (!ret) - RARCH_ERR("%s \"%s\".\n", + RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_SAVE_STATE_TO), path); @@ -451,7 +452,7 @@ static bool load_content_need_fullpath( /** * load_content: * @special : subsystem of content to be loaded. Can be NULL. - * content : + * content : * * Load content file (for libretro core). * @@ -462,6 +463,7 @@ static bool load_content(const struct retro_subsystem_info *special, { unsigned i; bool ret = true; + const char* json = NULL; struct string_list* additional_path_allocs = string_list_new(); struct retro_game_info *info = (struct retro_game_info*) calloc(content->size, sizeof(*info)); @@ -487,7 +489,7 @@ static bool load_content(const struct retro_subsystem_info *special, } info[i].path = NULL; - + if (*path) info[i].path = path; @@ -510,7 +512,18 @@ static bool load_content(const struct retro_subsystem_info *special, if (special) ret = core.retro_load_game_special(special->id, info, content->size); else + { + if (*content->elems[0].data) + { + if ( cheevos_get_by_content(&json, info->data, info->size, CHEEVOS_FLAGS_IS_SNES) == 0 ) + { + cheevos_load(json); + free((void*)json); + } + } + ret = core.retro_load_game(*content->elems[0].data ? info : NULL); + } if (!ret) RARCH_ERR("%s.\n", msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); diff --git a/griffin/griffin.c b/griffin/griffin.c index 3661b9c890..ac94854b21 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -79,7 +79,12 @@ CHEATS /*============================================================ ACHIEVEMENTS ============================================================ */ +#if defined(HAVE_CHEEVOS) && !defined(HAVE_NETPLAY) +#include "../libretro-common/net/net_http.c" +#endif + #include "../libretro-common/formats/json/jsonsax.c" +#include "../libretro-common/utils/md5.c" #include "../cheevos.c" /*============================================================