/* compiled with: * clang `pkg-config --libs json-c libcurl ncursesw` fm.c */ #include #include #include #include #include #include #include #include #include #include #define MAX_NAME 15 #define ESC_FIN "\x1b[0m" #define GREEN_PAIR (short)1 #define RED_PAIR (short)2 #define YELLOW_BG (short)3 #define RED_BG (short)4 #define m_mvaddstr(y, x, z) mvaddstr(y, x-(strlen(z)/2), z) #define l_mvaddstr(y, x, z) mvaddstr(y, x-strlen(z), z) /* Copy of libcurl docs getinmemory.c + main */ struct MemoryStruct { char *memory; size_t size; }; static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)userp; char *ptr = realloc(mem->memory, mem->size + realsize + 1); if (!ptr) { printf("not enough memory (realloc returned NULL\n)"); return 0; } mem->memory = ptr; memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; return realsize; } /* Evaluate if key "shortName" exists */ bool has_shortname(json_object *player) { json_object *sn = json_object_object_get(player, "shortName"); if (json_object_is_type(sn, json_type_null)) return 0; else return 1; } /* WARNING: CRIME AGAINST HUMANITY IN THE NEXT FUNCTION */ /* Display performance info next to the players name */ void add_info(json_object *p, size_t strl, int mx, int y, bool left) { json_object *events = json_object_object_get(p, "events"); json_object *g = json_object_object_get(events, "g"); json_object *as = json_object_object_get(events, "as"); json_object *yc = json_object_object_get(events, "yc"); json_object *rc = json_object_object_get(events, "rc"); json_object *sub = json_object_object_get(events, "sub"); json_object *subbedIn = json_object_object_get(sub, "subbedIn"); json_object *subbedOut = json_object_object_get(sub, "subbedOut"); if (left) { int offset = 0; if (subbedIn) { char *str = malloc(7); memset(str, 0, 7); sprintf(str, "> %d ", json_object_get_int(subbedIn)); offset += strlen(str); attron(COLOR_PAIR(GREEN_PAIR)); mvaddstr(y, mx-strl-offset, str); attroff(COLOR_PAIR(GREEN_PAIR)); } if (subbedOut) { // __ai_subbed(subbedOut, strl, mx, y, off, false, left); char *str = malloc(7); memset(str, 0, 7); sprintf(str, "< %d ", json_object_get_int(subbedOut)); offset += strlen(str); attron(COLOR_PAIR(RED_PAIR)); mvaddstr(y, mx-strl-offset, str); attroff(COLOR_PAIR(RED_PAIR)); } if (yc) { for (int i = 0; i < json_object_get_int(yc); i++) { offset += 2; attron(COLOR_PAIR(YELLOW_BG)); mvaddch(y, mx-strl-offset, ' '); attroff(COLOR_PAIR(YELLOW_BG)); } } if (rc) { attron(COLOR_PAIR(RED_BG)); offset += 2; mvaddch(y, mx-strl-offset-2, ' '); attroff(COLOR_PAIR(RED_BG)); } if (g) { int goals = json_object_get_int(g); char *str = malloc(goals+1); str[goals] = 0; offset += goals+1; memset(str, 'O', goals); mvaddstr(y, mx-strl-offset, str); } if (as) { int assists = json_object_get_int(as); char *str = malloc(assists)+1; str[assists] = 0; offset += assists+1; memset(str, 'a', assists); mvaddstr(y, mx-strl-offset, str); } } else { int offset = 0; if (subbedIn) { char *str = malloc(7); memset(str, 0, 7); sprintf(str, " > %d", json_object_get_int(subbedIn)); attron(COLOR_PAIR(GREEN_PAIR)); mvaddstr(y, mx+strl+offset, str); offset += strlen(str); attroff(COLOR_PAIR(GREEN_PAIR)); } if (subbedOut) { char *str = malloc(7); memset(str, 0, 7); sprintf(str, " < %d", json_object_get_int(subbedOut)); attron(COLOR_PAIR(RED_PAIR)); mvaddstr(y, mx+strl+offset, str); offset += strlen(str); attroff(COLOR_PAIR(RED_PAIR)); } if (yc) { for (int i = 0; i < json_object_get_int(yc); i++) { offset++; attron(COLOR_PAIR(YELLOW_BG)); mvaddch(y, mx+strl+offset, ' '); attroff(COLOR_PAIR(YELLOW_BG)); offset++; } } if (rc) { attron(COLOR_PAIR(RED_BG)); mvaddch(y, mx+strl+offset+2, ' '); attroff(COLOR_PAIR(RED_BG)); offset += 2; } if (g) { int goals = json_object_get_int(g); char *str = malloc(goals+1); memset(str, 'O', goals); str[goals] = 0; offset += goals+1; mvaddstr(y, mx+strl+offset, str); } if (as) { int assists = json_object_get_int(as); char *str = malloc(assists)+1; memset(str, 'a', assists); str[assists] = 0; offset += assists+1; mvaddstr(y, mx+strl+offset, str); } } } /* Place player name and match info */ void put_player(json_object *p, int mx, int y, bool left) { char *str = malloc(50); json_object *name_obj; if (has_shortname(p)) { name_obj = json_object_object_get(p, "shortName"); } else { name_obj = json_object_object_get(p, "name"); name_obj = json_object_object_get(name_obj, "lastName"); } json_object *pos = json_object_object_get(p, "positionStringShort"); json_object *shirt = json_object_object_get(p, "shirt"); if (left) { sprintf(str, "%s %s %2d ", json_object_get_string(name_obj), (pos) ? json_object_get_string(pos) : "" , json_object_get_int(shirt)); mvaddstr(y, mx-strlen(str), str); add_info(p, strlen(str), mx, y, left); } else { sprintf(str, " %2d %s %s", json_object_get_int(shirt), (pos) ? json_object_get_string(pos) : "", json_object_get_string(name_obj)); mvaddstr(y, mx, str); add_info(p, strlen(str), mx, y, left); } } void __lfj(json_object *teams, int mx) { for (int i = 0; i < json_object_array_length(teams); i++) { int l = 2; json_object *team = json_object_array_get_idx(teams, i); json_object *players = json_object_object_get(team, "players"); for (int line_idx = 0; line_idx < json_object_array_length(players); line_idx++) { json_object *line = json_object_array_get_idx(players, line_idx); for (int play_idx = 0; play_idx < json_object_array_length(line); play_idx++) { json_object *p = json_object_array_get_idx(line, play_idx); put_player(p, mx, l++, !i); } } l += 2; json_object *bench = json_object_object_get(team, "bench"); for (int b = 0; b < json_object_array_length(bench); b++) { json_object *p = json_object_array_get_idx(bench, b); // if (json_object_object_get(p, "timeSubbedOn")) put_player(p, mx, l++, !i); } } } /* Extract information from lineup and display in curses */ void lineup_from_json(WINDOW *w, char *json_raw) { json_object *root = json_tokener_parse(json_raw); if (!root) return; int mx = w->_maxx / 2; json_object *matchName = json_object_object_get(json_object_object_get(root, "general"), "matchName"); m_mvaddstr(0, mx, json_object_get_string(matchName)); json_object *content = json_object_object_get(root, "content"); json_object *lineup = json_object_object_get(content, "lineup"); lineup = json_object_object_get(lineup, "lineup"); __lfj(lineup, mx); json_object_put(root); } /* Collect match data from URL to DEST */ char* data_from_url(char *dest, char *url, CURL *curl_handle) { CURLcode res; struct MemoryStruct chunk; chunk.memory = malloc(1); chunk.size = 0; curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); res = curl_easy_perform(curl_handle); char *tmpdest; if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } else { tmpdest = realloc(dest, chunk.size); dest = tmpdest; memcpy(dest, chunk.memory, chunk.size); } curl_easy_cleanup(curl_handle); free(chunk.memory); return dest; } int main(int argc, char **argv) { setlocale(LC_ALL, "en_US.UTF-8"); int opt, id; while ((opt = getopt(argc, argv, "i:s:")) != -1) { switch (opt) { case 'i': id = atoi(optarg); if (!id) return 1; break; } } WINDOW *w = initscr(); curs_set(0); CURL *curl_handle; char *content; char *url = malloc(100); sprintf(url, "https://www.fotmob.com/api/matchDetails?matchId=%d", id); content = data_from_url(content, url, curl_handle); start_color(); init_pair(GREEN_PAIR, COLOR_GREEN, COLOR_BLACK); init_pair(RED_PAIR, COLOR_RED, COLOR_BLACK); init_pair(YELLOW_BG, COLOR_WHITE, COLOR_YELLOW); init_pair(RED_BG, COLOR_WHITE, COLOR_RED); lineup_from_json(w, content); refresh(); sleep(5); clear(); endwin(); free(content); curl_global_cleanup(); return 0; }