shokara's own minecraft launcher
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 
 

264 lines
5.7 KiB

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cJSON/cJSON.h>
#include "somc.h"
#include "fetch.h"
#define VERSION_MANIFEST_URL "https://launchermeta.mojang.com/mc/game/version_manifest.json"
static char manifest_path[32];
static char version_path[32];
static int get_manifest_csv(int overwrite);
static int get_version_csv(char *version_id, int overwrite);
/*
* get_manifest_csv() fetches version_manifest.json from VERSION_MANIFEST_URL
* via curl and then parses that json into a csv file.
* The csv file's lines are formatted as:
* id, type, url
*
* An example is:
* b1.7.3,old_beta,https://url.to.minecraft/b1.7.3.json
*/
static int get_manifest_csv(int overwrite)
{
FILE *fp;
char path[32];
char *data;
size_t len;
cJSON *json, *versions_arr, *version_obj;
strncpy(path, ".somc.version_manifest.json", sizeof(path));
strncpy(manifest_path, "versions/", sizeof(manifest_path));
mkdir(manifest_path, 0755);
strncat(manifest_path, "versions.csv", sizeof(manifest_path) - strlen(manifest_path) - 1);
/* exit if the csv already exists and overwrite flag isn't 0 */
if (overwrite != 0 && (fp = fopen(manifest_path, "r")) != NULL) {
fseek(fp, 0, SEEK_END);
if (ftell(fp) > 0) {
fprintf(stderr, "%s already exists", manifest_path);
return 0;
}
fclose(fp);
}
if (fetch_file(VERSION_MANIFEST_URL, path, overwrite) != 0) {
fprintf(stderr, "minecraft version manifest failed to download\n");
return -1;
}
if ((fp = fopen(path, "r")) == NULL) {
perror("fopen for version manifest failed");
return -2;
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
rewind(fp);
if ((data = malloc(len)) == NULL) {
perror("malloc for data failed");
fclose(fp);
return -3;
}
fread(data, 1, len, fp);
fclose(fp);
unlink(path);
json = cJSON_ParseWithLength(data, len);
free(data);
if (json == NULL) {
const char *error = cJSON_GetErrorPtr();
if (error != NULL)
fprintf(stderr, "json error before: %s\n", error);
cJSON_Delete(json);
return -4;
}
if ((fp = fopen(manifest_path, "w")) == NULL) {
perror("fopen for versions.csv failed");
return -2;
}
versions_arr = cJSON_GetObjectItemCaseSensitive(json, "versions");
cJSON_ArrayForEach(version_obj, versions_arr) {
cJSON *id, *type, *url;
id = cJSON_GetObjectItemCaseSensitive(version_obj, "id");
type = cJSON_GetObjectItemCaseSensitive(version_obj, "type");
url = cJSON_GetObjectItemCaseSensitive(version_obj, "url");
fprintf(fp, "%s,%s,%s\n", id->valuestring, type->valuestring,
url->valuestring);
}
cJSON_Delete(json);
fclose(fp);
return 0;
}
static int get_version_csv(char *version_id, int overwrite)
{
FILE *fp;
char version_json_path[32], path[32], line[256];
char *url = NULL, *data;
size_t len;
cJSON *json, *mainClass, *libraries, *library;
strncpy(version_json_path, ".somc.version.json", sizeof(version_json_path));
strncpy(path, "versions/", sizeof(version_json_path));
strncat(path, version_id, sizeof(version_json_path)
- strlen(version_json_path) - 1);
strncat(path, ".csv", sizeof(version_json_path)
- strlen(version_json_path) - 1);
if (get_manifest_csv(0) < 0)
return -1;
if ((fp = fopen(manifest_path, "r")) == NULL)
return -2;
/* get the version's json url */
while (fgets(line, sizeof(line), fp) != NULL) {
size_t len;
if (strstr(line, version_id) == NULL)
continue;
strtok(line, ",");
strtok(NULL, ",");
if ((url = strtok(NULL, ",")) == NULL) {
fclose(fp);
return -3;
}
len = strlen(url);
if (url[len - 1] == '\n')
url[len - 1] = '\0';
break;
}
if (url == NULL)
return -3;
if (fetch_file(url, version_json_path, overwrite) != 0) {
fprintf(stderr, "minecraft version json failed to download\n");
return -1;
}
if ((fp = fopen(version_json_path, "r")) == NULL) {
perror("fopen for version manifest failed");
return -2;
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
rewind(fp);
if ((data = malloc(len)) == NULL) {
perror("malloc for data failed");
fclose(fp);
return -3;
}
fread(data, 1, len, fp);
fclose(fp);
unlink(version_json_path);
json = cJSON_ParseWithLength(data, len);
free(data);
if (json == NULL) {
const char *error = cJSON_GetErrorPtr();
if (error != NULL)
fprintf(stderr, "json error before: %s\n", error);
cJSON_Delete(json);
return -4;
}
if ((fp = fopen(manifest_path, "w")) == NULL) {
perror("fopen for versions.csv failed");
return -2;
}
libraries = cJSON_GetObjectItemCaseSensitive(json, "versions");
cJSON_ArrayForEach(version_obj, versions_arr) {
cJSON *id, *type, *url;
id = cJSON_GetObjectItemCaseSensitive(version_obj, "id");
type = cJSON_GetObjectItemCaseSensitive(version_obj, "type");
url = cJSON_GetObjectItemCaseSensitive(version_obj, "url");
fprintf(stdout, "%s,%s,%s\n", id->valuestring, type->valuestring,
url->valuestring);
}
cJSON_Delete(json);
fclose(fp);
return 0;
}
int list_versions(void)
{
FILE *fp;
char line[256];
if (get_manifest_csv(0) < 0)
return -1;
if ((fp = fopen(manifest_path, "r")) == NULL) {
perror("fopen for versions.csv failed");
return -2;
}
while (fgets(line, sizeof(line), fp) != NULL) {
char *id, *type;
id = strtok(line, ",");
type = strtok(NULL, ",");
printf("%s (%s)\n", id, type);
}
return 0;
}
int instance_create(char *name, char *version_id)
{
char instances_dir[256];
FILE *manifest_fp;
char *manifest_data;
size_t manifest_len;
if (get_version_csv(version_id, 0) < 0)
return -1;
strncpy(instances_dir, "instances/", sizeof(instances_dir));
mkdir(instances_dir, 0755);
strncat(instances_dir, name, sizeof(instances_dir)
- strlen(instances_dir) - 1);
mkdir(instances_dir, 0755);
chdir(instances_dir);
return 0;
}