Ver código fonte

Command line options for the JSON updater. The updating functionality has been added to the GUI.

Iñigo Valentin 4 anos atrás
pai
commit
8678a23c37

+ 3 - 0
src/RuneOptimizer/RuneOptimizer.h

@@ -17,6 +17,9 @@
 
 #define DB_NAME "data.sqlite"
 
+#define TRUE 1
+#define FALSE 0
+
 char db_location[256];
 
 struct Rune_Set_Count {

+ 7 - 1
src/RuneOptimizer/help/help.c

@@ -26,12 +26,18 @@ void show_help(){
     printf("\n    Display this help text and exists. It has no options.\n");
     printf("\n\n  Command: update\n");
     printf("\n    Updates the information and builds a database. Usage.\n");
-    printf("    RuneOptimizer update [source]\n");
+    printf("    RuneOptimizer update [source] [options]\n");
     printf("\n      [source] can be either: \n");
     printf("        A JSON file exported from the game: \n");
     printf("        A SQLite file from a SWDB instance (unimplemented)\n");
     printf("        A SWDB profile URL (unimplemented)\n");
     printf("        A Swarfarm profile URL (unimplemented)\n");
+    printf("        Options: \n\n");
+    printf("          -s | --six-stars     Only save the info about the 6 star units.\n");
+    printf("                               If a unit has runes, it will be saved anyway.\n");
+    printf("          -r | --with-runes    Only save info about units with runes.\n");
+    printf("          -t | --clear-teams   Delete all team information (can't be undone!).\n");
+    printf("          -g | --gui           Formats the output to be consumed by the GUI.\n");
     printf("\n\n  Command: team\n");
     printf("\n    Manages teams.\n");
     printf("\n    Usage\n");

+ 33 - 14
src/RuneOptimizer/optimize/optimize.c

@@ -526,7 +526,9 @@ int parse_optimization_arguments(
  */
 int retrieveUnit(char id[64], struct Unit *unit){
     sqlite3 *db;
-    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (
+      SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)
+    ){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;
@@ -796,7 +798,9 @@ void createQueryForEvenSlots(
     // Excluded teams
     strcat(query, " AND (unit = '' OR unit = '");
     strcat(query, unit_id);
-    strcat(query, "' OR unit NOT IN (SELECT unit FROM units_teams WHERE team IN(");
+    strcat(
+      query, "' OR unit NOT IN (SELECT unit FROM units_teams WHERE team IN("
+    );
     for (int i = 0; i < 64 && excluded_teams[i][0] != '\0'; i ++){
         strcat(query, "'");
         strcat(query, excluded_teams[i]);
@@ -887,7 +891,9 @@ void createQueryForOddSlots(
     // Excluded teams
     strcat(query, " AND (unit = '' OR unit = '");
     strcat(query, unit_id);
-    strcat(query, "' OR unit NOT IN (SELECT unit FROM units_teams WHERE team IN(");
+    strcat(
+      query, "' OR unit NOT IN (SELECT unit FROM units_teams WHERE team IN("
+    );
     for (int i = 0; i < 64 && excluded_teams[i][0] != '\0'; i ++){
         strcat(query, "'");
         strcat(query, excluded_teams[i]);
@@ -929,7 +935,9 @@ int get_runes(
     char **query;
     sqlite3 *db;
     sqlite3_stmt *stmt_runes[7];
-    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (
+      SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)
+    ){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;
@@ -941,8 +949,11 @@ int get_runes(
         else{
             query = &query_odd;
         }
-        if (SQLITE_OK != sqlite3_prepare_v2(db, *query, -1, &stmt_runes[i], 0)) {
-            fprintf(stderr, "Error getting runes for slot %d: %s\n", i, sqlite3_errmsg(db));
+        if (SQLITE_OK != sqlite3_prepare_v2(db, *query, -1, &stmt_runes[i], 0)){
+            fprintf(
+              stderr,
+              "Error getting runes for slot %d: %s\n", i, sqlite3_errmsg(db)
+            );
             sqlite3_close(db);
             return ERROR_DB_RUNES_SLOT;
         }
@@ -1770,14 +1781,22 @@ int optimize(int argc, char *argv[]){
                 // RES: 1 rating point per 1.52 points
                 // ACC: 1 rating point per 1.52 points
                 float rating = 0.0f;
-                rating += (float) ((int) stats.hp  - (int) unit.current_hp ) / 58.29f;
-                rating += (float) ((int) stats.atk - (int) unit.current_atk) /  1.50f;
-                rating += (float) ((int) stats.def - (int) unit.current_def) /  1.50f;
-                rating += (float) ((int) stats.spd - (int) unit.current_spd) /  1.00f;
-                rating += (float) ((int) stats.crr - (int) unit.current_crr) /  1.38f;
-                rating += (float) ((int) stats.crd - (int) unit.current_crd) /  1.90f;
-                rating += (float) ((int) stats.res - (int) unit.current_res) /  1.52f;
-                rating += (float) ((int) stats.acc - (int) unit.current_acc) /  1.52f;
+                rating +=
+                  (float) ((int) stats.hp  - (int) unit.current_hp ) / 58.29f;
+                rating +=
+                  (float) ((int) stats.atk - (int) unit.current_atk) /  1.50f;
+                rating +=
+                  (float) ((int) stats.def - (int) unit.current_def) /  1.50f;
+                rating +=
+                  (float) ((int) stats.spd - (int) unit.current_spd) /  1.00f;
+                rating +=
+                  (float) ((int) stats.crr - (int) unit.current_crr) /  1.38f;
+                rating +=
+                  (float) ((int) stats.crd - (int) unit.current_crd) /  1.90f;
+                rating +=
+                  (float) ((int) stats.res - (int) unit.current_res) /  1.52f;
+                rating +=
+                  (float) ((int) stats.acc - (int) unit.current_acc) /  1.52f;
                 results[result_count].rating = (int) rating;
 
                 // Account for found result

+ 3 - 1
src/RuneOptimizer/team/team.c

@@ -34,7 +34,9 @@ int list_teams(int argc, char *argv[]){
     }
     strcat(query, " ORDER BY priority DESC");
     sqlite3 *db;
-    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (
+      SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)
+    ){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;

+ 291 - 113
src/RuneOptimizer/update/update.c

@@ -24,21 +24,51 @@
  */
 int update(int argc, char *argv[]){
 
+    // Options
+    char six_stars = FALSE;
+    char with_runes = FALSE;
+    char clear_teams = FALSE;
+    char gui = FALSE;
+
     // One argument mandatory
     if (argc == 0){
         fprintf(
           stderr,
-          "Team creation takes 1 or 2 arguments, %d supplied\n", argc - 3
+          "Team creation takes 1 or more arguments, %d supplied\n", argc - 3
         );
         return ERROR_INPUT_UPDATE_NO_ARGUMENTS;
     }
 
+    // Parse options, skip first arg
+    for (int i = 1; i < argc; i ++){
+        if (strcmp("--six-stars", argv[i]) == 0 || strcmp("-s", argv[i]) == 0){
+            six_stars = TRUE;
+        }
+        else if (
+          strcmp("--with-runes", argv[i]) == 0 || strcmp("-r", argv[i]) == 0
+        ){
+            with_runes = TRUE;
+        }
+        else if (
+          strcmp("--clear-teams", argv[i]) == 0 || strcmp("-t", argv[i]) == 0
+        ){
+            clear_teams = TRUE;
+        }
+        else if (
+          strcmp("--gui", argv[i]) == 0 || strcmp("-g", argv[i]) == 0
+        ){
+            gui = TRUE;
+        }
+    }
+
     // First, test json
     if (strlen(argv[0]) > 5){
         char extension[6];
         strncpy(extension, argv[0] + strlen(argv[0]) - 5, 5);
         if (strcmp(extension, ".json") == 0){
-            return update_json(argv[0]);
+            return update_json(
+              argv[0], six_stars, with_runes, clear_teams, gui
+            );
         }
     }
     // TODO: More ifs as more options are implemented...
@@ -50,10 +80,18 @@ int update(int argc, char *argv[]){
  * Updates the database from a json file.
  *
  * @param[in] file Path to the file.
- * @return SUCCESS or an error code..
+ * @param[in] six_stars Indicator to import only 6* units. Units with runes will
+ * be saved anyway.
+ * @param[in] with_runes Indicator to import only units with runes.
+ * @param[in] clear_teams Indicator to recreate tables teams and units_teams.
+ * @param[in] gui Indicator to format output for thr GUI.
+ * @return SUCCESS or an error code.
  */
-int update_json(char file[]){
-    printf("Updating from json file: %s\n", file);
+int update_json(
+  char file[], unsigned char six_stars,
+  unsigned char with_runes, unsigned char clear_teams, unsigned char gui
+){
+    if (gui == FALSE) printf("Updating from json file: %s\n", file);
 
     // Read the JSON content
     json_object *root = json_object_from_file(file);
@@ -63,23 +101,34 @@ int update_json(char file[]){
     }
 
     json_object *wizard_id = json_object_object_get(root, "wizard_id");
-    printf("\tPlayer ID: %s\n", json_object_get_string(wizard_id));
 
     json_object *wizard_info = json_object_object_get(root, "wizard_info");
     json_object *wizard_name = json_object_object_get(wizard_info, "wizard_name");
-    printf("\tPlayer name: %s\n", json_object_get_string(wizard_name));
-    json_object *wizard_level = json_object_object_get(wizard_info, "wizard_level");
-    printf("\tPlayer level: %s\n", json_object_get_string(wizard_level));
+    json_object *wizard_level =
+      json_object_object_get(wizard_info, "wizard_level");
+    if (gui == TRUE){
+        printf("Player ID: %s\n", json_object_get_string(wizard_id));
+        printf("Player name: %s\n", json_object_get_string(wizard_name));
+        printf("Player level: %s\n", json_object_get_string(wizard_level));
+        fflush(stdout);
+    }
+    else{
+        printf("\tPlayer ID: %s\n", json_object_get_string(wizard_id));
+        printf("\tPlayer name: %s\n", json_object_get_string(wizard_name));
+        printf("\tPlayer level: %s\n", json_object_get_string(wizard_level));
+    }
 
     // Recreate the database tables
-    int status = recreate_tables();
+    int status = recreate_tables(clear_teams);
     if (SUCCESS != status){
         return status;
     }
 
     // Open the database connection
     sqlite3 *db;
-    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (
+      SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)
+    ){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;
@@ -146,7 +195,6 @@ int update_json(char file[]){
             rune.stats[i].enchant = json_object_get_int(enchant);
             json_object *grind = json_object_array_get_idx(stat_idx, 3);
             rune.stats[i].grind = json_object_get_int(grind);
-            //printf("\t\t\tStat %d: %d: (+%d, %d)\n", json_object_get_int(stat), json_object_get_int(value), json_object_get_int(grind), json_object_get_int(enchant));
         }
 
         // All the rune info has been loaded. Now, calculate the rest of stats
@@ -381,15 +429,17 @@ int update_json(char file[]){
 
     // Parse units
     unsigned int total_units = 0;
+    unsigned char unit_has_runes = FALSE;
     json_object *unit_list = json_object_object_get(root, "unit_list");
     int unit_count = json_object_array_length(unit_list);
     for (int i = 0; i < unit_count; i++){
-        total_units ++;
+        unit_has_runes = FALSE;
         struct DB_Unit unit;
         idx = json_object_array_get_idx(unit_list, i);
         json_object *unit_id = json_object_object_get(idx, "unit_id");
         strcpy(unit.id, json_object_get_string(unit_id));
-        json_object *unit_master_id = json_object_object_get(idx, "unit_master_id");
+        json_object *unit_master_id =
+          json_object_object_get(idx, "unit_master_id");
         unit.monster= json_object_get_int(unit_master_id);
         get_monster_name(unit.monster, unit.name);
         json_object *unit_level = json_object_object_get(idx, "unit_level");
@@ -410,95 +460,24 @@ int update_json(char file[]){
         unit.base_res = json_object_get_int(resist);
         json_object *accuracy = json_object_object_get(idx, "accuracy");
         unit.base_acc = json_object_get_int(accuracy);
-        json_object *critical_rate = json_object_object_get(idx, "critical_rate");
+        json_object *critical_rate =
+          json_object_object_get(idx, "critical_rate");
         unit.base_crr = json_object_get_int(critical_rate);
-        json_object *critical_damage = json_object_object_get(idx, "critical_damage");
+        json_object *critical_damage =
+          json_object_object_get(idx, "critical_damage");
         unit.base_crd = json_object_get_int(critical_damage);
-        json_object *homunculus_name = json_object_object_get(idx, "homunculus_name");
+        json_object *homunculus_name =
+          json_object_object_get(idx, "homunculus_name");
         if (strlen(json_object_get_string(homunculus_name)) > 0){
             strcpy(unit.name, json_object_get_string(homunculus_name));
         }
 
-        // Insert unit
-        sqlite3_stmt *res;
-        char sql[2000];
-        strcpy(
-        sql,
-        "INSERT INTO units ("
-        "  id,"
-        "  monster,"
-        "  name,"
-        "  stars,"
-        "  level,"
-        "  base_hp,"
-        "  base_atk,"
-        "  base_def,"
-        "  base_spd,"
-        "  base_crr,"
-        "  base_crd,"
-        "  base_res,"
-        "  base_acc,"
-        "  current_hp,"
-        "  current_atk,"
-        "  current_def,"
-        "  current_spd,"
-        "  current_crr,"
-        "  current_crd,"
-        "  current_res,"
-        "  current_acc"
-        ") VALUES ("
-        "  ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
-        ")"
-        );
-        if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
-            fprintf(
-            stderr,
-            "Failed to prepare statement to insert into runes: %s\n",
-            sqlite3_errmsg(db)
-            );
-            sqlite3_finalize(res);
-            sqlite3_close(db);
-            return ERROR_DB_INSERT_RUNES;
-        }
-        sqlite3_bind_text(res, 1, unit.id, strlen(unit.id), NULL);
-        sqlite3_bind_int(res, 2, unit.monster);
-        sqlite3_bind_text(res, 3, unit.name, strlen(unit.name), NULL);
-        sqlite3_bind_int(res, 4, unit.stars);
-        sqlite3_bind_int(res, 5, unit.level);
-        sqlite3_bind_int(res, 6, unit.base_hp);
-        sqlite3_bind_int(res, 7, unit.base_atk);
-        sqlite3_bind_int(res, 8, unit.base_def);
-        sqlite3_bind_int(res, 9, unit.base_spd);
-        sqlite3_bind_int(res, 10, unit.base_crr);
-        sqlite3_bind_int(res, 11, unit.base_crd);
-        sqlite3_bind_int(res, 12, unit.base_res);
-        sqlite3_bind_int(res, 13, unit.base_acc);
-        // Current stats, instert as base, will be calculated later.
-        sqlite3_bind_int(res, 14, unit.base_atk);
-        sqlite3_bind_int(res, 15, unit.base_atk);
-        sqlite3_bind_int(res, 16, unit.base_def);
-        sqlite3_bind_int(res, 17, unit.base_spd);
-        sqlite3_bind_int(res, 18, unit.base_crr);
-        sqlite3_bind_int(res, 19, unit.base_crd);
-        sqlite3_bind_int(res, 20, unit.base_res);
-        sqlite3_bind_int(res, 21, unit.base_acc);
-        if (SQLITE_DONE != sqlite3_step(res)){
-            fprintf(
-            stderr,
-            "Failed to execute statement to insert into units: %s\n",
-            sqlite3_errmsg(db)
-            );
-            sqlite3_finalize(res);
-            sqlite3_close(db);
-            return ERROR_DB_INSERT_RUNES;
-        }
-        sqlite3_finalize(res);
-
         // Parse unit runes
         json_object *unit_runes = json_object_object_get(idx, "runes");
         rune_count = json_object_array_length(unit_runes);
 
         for (int i = 0; i < rune_count; i++){
+            unit_has_runes = TRUE;
             total_runes ++;
             struct DB_Rune rune;
             strcpy(rune.unit, unit.id);
@@ -516,7 +495,8 @@ int update_json(char file[]){
             json_object *set_id = json_object_object_get(idx, "set_id");
             rune.set = json_object_get_int(set_id);
             // upgrade_curre -> Power up level
-            json_object *upgrade_curr = json_object_object_get(idx, "upgrade_curr");
+            json_object *upgrade_curr =
+              json_object_object_get(idx, "upgrade_curr");
             rune.level = json_object_get_int(upgrade_curr);
             json_object *base_value = json_object_object_get(idx, "base_value");
             rune.buy_value = json_object_get_int(base_value);
@@ -535,7 +515,8 @@ int update_json(char file[]){
             // Innate stat
             json_object *prefix_stat = json_object_array_get_idx(prefix_eff, 0);
             rune.innate.stat = json_object_get_int(prefix_stat);
-            json_object *prefix_value = json_object_array_get_idx(prefix_eff, 1);
+            json_object *prefix_value =
+              json_object_array_get_idx(prefix_eff, 1);
             rune.innate.value = json_object_get_int(prefix_value);
 
             json_object *sec_eff = json_object_object_get(idx, "sec_eff");
@@ -555,7 +536,8 @@ int update_json(char file[]){
                 rune.stats[i].grind = json_object_get_int(grind);
             }
 
-            // All the rune info has been loaded. Now, calculate the rest of stats
+            // All the rune info has been loaded. Now, calculate the rest of
+            // stats
             calculate_rune_totals(&rune);
 
             // Insert the rune into the database
@@ -608,8 +590,8 @@ int update_json(char file[]){
             "  lv15_acc,"
             "  lv15_res"
             ") VALUES ("
-            "  ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"
-            "  ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
+            "  ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"
+            "  ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
             ")"
             );
             if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
@@ -722,7 +704,8 @@ int update_json(char file[]){
                 if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
                     fprintf(
                     stderr,
-                    "Failed to prepare statement to insert into rune_stats: %s\n",
+                    "Failed to prepare statement to "
+                    "insert into rune_stats: %s\n",
                     sqlite3_errmsg(db)
                     );
                     sqlite3_finalize(res);
@@ -738,7 +721,8 @@ int update_json(char file[]){
                 if (SQLITE_DONE != sqlite3_step(res)){
                     fprintf(
                     stderr,
-                    "Failed to execute statement to insert into rune_stats: %s\n",
+                    "Failed to execute statement to "
+                    "insert into rune_stats: %s\n",
                     sqlite3_errmsg(db)
                     );
                     sqlite3_finalize(res);
@@ -758,7 +742,8 @@ int update_json(char file[]){
                 if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
                     fprintf(
                     stderr,
-                    "Failed to prepare statement to insert into rune_stats: %s\n",
+                    "Failed to prepare statement to "
+                    "insert into rune_stats: %s\n",
                     sqlite3_errmsg(db)
                     );
                     sqlite3_finalize(res);
@@ -774,7 +759,8 @@ int update_json(char file[]){
                 if (SQLITE_DONE != sqlite3_step(res)){
                     fprintf(
                     stderr,
-                    "Failed to execute statement to insert into rune_stats: %s\n",
+                    "Failed to execute statement to "
+                    "insert into rune_stats: %s\n",
                     sqlite3_errmsg(db)
                     );
                     sqlite3_finalize(res);
@@ -785,6 +771,90 @@ int update_json(char file[]){
             sqlite3_finalize(res);
         }
 
+        // Insert unit, depending on flags and status
+        if (
+            unit_has_runes == TRUE ||
+            (six_stars == FALSE && with_runes == FALSE) ||
+            (six_stars == TRUE && unit.stars == TRUE)
+        ){
+            total_units ++;
+            sqlite3_stmt *res;
+            char sql[2000];
+            strcpy(
+              sql,
+              "INSERT INTO units ("
+              "  id,"
+              "  monster,"
+              "  name,"
+              "  stars,"
+              "  level,"
+              "  base_hp,"
+              "  base_atk,"
+              "  base_def,"
+              "  base_spd,"
+              "  base_crr,"
+              "  base_crd,"
+              "  base_res,"
+              "  base_acc,"
+              "  current_hp,"
+              "  current_atk,"
+              "  current_def,"
+              "  current_spd,"
+              "  current_crr,"
+              "  current_crd,"
+              "  current_res,"
+              "  current_acc,"
+              "  storage"
+              ") VALUES ("
+              "  ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"
+              "  ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
+              ")"
+            );
+            if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
+                fprintf(
+                  stderr,
+                  "Failed to prepare statement to insert into units: %s\n",
+                  sqlite3_errmsg(db)
+                  );
+                sqlite3_finalize(res);
+                sqlite3_close(db);
+                return ERROR_DB_INSERT_RUNES;
+            }
+            sqlite3_bind_text(res, 1, unit.id, strlen(unit.id), NULL);
+            sqlite3_bind_int(res, 2, unit.monster);
+            sqlite3_bind_text(res, 3, unit.name, strlen(unit.name), NULL);
+            sqlite3_bind_int(res, 4, unit.stars);
+            sqlite3_bind_int(res, 5, unit.level);
+            sqlite3_bind_int(res, 6, unit.base_hp);
+            sqlite3_bind_int(res, 7, unit.base_atk);
+            sqlite3_bind_int(res, 8, unit.base_def);
+            sqlite3_bind_int(res, 9, unit.base_spd);
+            sqlite3_bind_int(res, 10, unit.base_crr);
+            sqlite3_bind_int(res, 11, unit.base_crd);
+            sqlite3_bind_int(res, 12, unit.base_res);
+            sqlite3_bind_int(res, 13, unit.base_acc);
+            // Current stats, instert as base, will be calculated later.
+            sqlite3_bind_int(res, 14, unit.base_atk);
+            sqlite3_bind_int(res, 15, unit.base_atk);
+            sqlite3_bind_int(res, 16, unit.base_def);
+            sqlite3_bind_int(res, 17, unit.base_spd);
+            sqlite3_bind_int(res, 18, unit.base_crr);
+            sqlite3_bind_int(res, 19, unit.base_crd);
+            sqlite3_bind_int(res, 20, unit.base_res);
+            sqlite3_bind_int(res, 21, unit.base_acc);
+            sqlite3_bind_int(res, 21, 0); // TODO: Storage
+            if (SQLITE_DONE != sqlite3_step(res)){
+                fprintf(
+                  stderr,
+                  "Failed to execute statement to insert into units: %s\n",
+                  sqlite3_errmsg(db)
+                );
+                sqlite3_finalize(res);
+                sqlite3_close(db);
+                return ERROR_DB_INSERT_RUNES;
+            }
+            sqlite3_finalize(res);
+        }
     }
 
     int calculate_result = calculate_current_stats();
@@ -796,8 +866,16 @@ int update_json(char file[]){
     time_t t = time(NULL);
     struct tm tm = *localtime(&t);
     char ts[130];
-    sprintf(ts, "%d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
-    //printf("now: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+    sprintf(
+      ts,
+      "%d-%02d-%02d %02d:%02d:%02d",
+      tm.tm_year + 1900,
+      tm.tm_mon + 1,
+      tm.tm_mday,
+      tm.tm_hour,
+      tm.tm_min,
+      tm.tm_sec
+    );
     sqlite3_stmt *stmt_info;
     char sql[1000];
     strcpy(
@@ -815,9 +893,15 @@ int update_json(char file[]){
         sqlite3_close(db);
         return ERROR_DB_INSERT_INFO;
     }
-    sqlite3_bind_text(stmt_info, 1, json_object_get_string(wizard_id), strlen(json_object_get_string(wizard_id)), NULL);
-    sqlite3_bind_text(stmt_info, 2, json_object_get_string(wizard_name), strlen(json_object_get_string(wizard_name)), NULL);
-    sqlite3_bind_int(stmt_info, 3, json_object_get_string(wizard_level));
+    sqlite3_bind_text(
+      stmt_info, 1, json_object_get_string(wizard_id),
+      strlen(json_object_get_string(wizard_id)), NULL
+    );
+    sqlite3_bind_text(
+      stmt_info, 2, json_object_get_string(wizard_name),
+      strlen(json_object_get_string(wizard_name)), NULL
+    );
+    sqlite3_bind_int(stmt_info, 3, atoi(json_object_get_string(wizard_level)));
     sqlite3_bind_text(stmt_info, 4, ts, strlen(ts), NULL);
     sqlite3_bind_int(stmt_info, 5, 0);
     if (SQLITE_DONE != sqlite3_step(stmt_info)){
@@ -835,12 +919,11 @@ int update_json(char file[]){
     printf("%d units saved.\n", total_units);
     printf("%d runes saved.\n", total_runes);
     printf("%d rune stats saved.\n", total_stats);
-    printf("Player info updated");
+    printf("Player info updated\n");
 
+    sqlite3_finalize(stmt_info);
     sqlite3_close(db);
-
-
-    return
+    return SUCCESS;
 }
 
 /**
@@ -1138,10 +1221,15 @@ int calculate_current_stats(){
 
 /**
  * Recreates the tables in the database.
+ *
+ * @param[in] clear_teams Indicator to recreate tables teams and units_teams.
+ * @return SUCCESS or an error code.
  */
-int recreate_tables(){
+int recreate_tables(unsigned char clear_teams){
     sqlite3 *db;
-    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (
+      SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)
+    ){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;
@@ -1324,6 +1412,7 @@ int recreate_tables(){
       "  name CHAR(128),"
       "  stars INT,"
       "  level INT,"
+      "  storage INT,"
       "  base_hp INT,"
       "  base_atk INT,"
       "  base_def INT,"
@@ -1415,7 +1504,96 @@ int recreate_tables(){
         sqlite3_close(db);
         return ERROR_DB_CREATE_INFO;
     }
-    // TODO: Other tables
+    // Tables teams and units_teams (optional)
+    if (clear_teams == TRUE){
+        strcpy(sql, "DROP TABLE IF EXISTS units_teams;");
+        if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
+            fprintf(
+              stderr,
+              "Failed to create statement to drop table units_teams: %s\n",
+              sqlite3_errmsg(db)
+            );
+            sqlite3_finalize(res);
+            sqlite3_close(db);
+            return ERROR_DB_DROP_UNITS_TEAMS;
+        }
+        if (SQLITE_DONE != sqlite3_step(res)){
+            fprintf(
+              stderr,
+              "Failed to execute statement to drop table units_teams: %s\n",
+              sqlite3_errmsg(db)
+            );
+            sqlite3_finalize(res);
+            sqlite3_close(db);
+            return ERROR_DB_DROP_UNITS_TEAMS;
+        }
+        strcpy(sql, "CREATE TABLE units_teams(unit CHAR(12), team CHAR(12));");
+        if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
+            fprintf(
+            stderr,
+            "Failed to create statement to create table units_teams: %s\n",
+            sqlite3_errmsg(db)
+            );
+            sqlite3_finalize(res);
+            sqlite3_close(db);
+            return ERROR_DB_CREATE_UNITS_TEAMS;
+        }
+        if (SQLITE_DONE != sqlite3_step(res)){
+            fprintf(
+            stderr,
+            "Failed to execute statement to create table units_teams: %s\n",
+            sqlite3_errmsg(db)
+            );
+            sqlite3_finalize(res);
+            sqlite3_close(db);
+            return ERROR_DB_CREATE_UNITS_TEAMS;
+        }
+        strcpy(sql, "DROP TABLE IF EXISTS teams;");
+        if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
+            fprintf(
+              stderr,
+              "Failed to create statement to drop table teams: %s\n",
+              sqlite3_errmsg(db)
+            );
+            sqlite3_finalize(res);
+            sqlite3_close(db);
+            return ERROR_DB_DROP_TEAMS;
+        }
+        if (SQLITE_DONE != sqlite3_step(res)){
+            fprintf(
+              stderr,
+              "Failed to execute statement to drop table teams: %s\n",
+              sqlite3_errmsg(db)
+            );
+            sqlite3_finalize(res);
+            sqlite3_close(db);
+            return ERROR_DB_DROP_TEAMS;
+        }
+        strcpy(
+          sql,
+          "CREATE TABLE teams(unit CHAR(12), name CHAR(50), priority INT);"
+        );
+        if (SQLITE_OK != sqlite3_prepare_v2(db, sql, -1, &res, 0)) {
+            fprintf(
+            stderr,
+            "Failed to create statement to create table teams: %s\n",
+            sqlite3_errmsg(db)
+            );
+            sqlite3_finalize(res);
+            sqlite3_close(db);
+            return ERROR_DB_CREATE_TEAMS;
+        }
+        if (SQLITE_DONE != sqlite3_step(res)){
+            fprintf(
+            stderr,
+            "Failed to execute statement to create table teams: %s\n",
+            sqlite3_errmsg(db)
+            );
+            sqlite3_finalize(res);
+            sqlite3_close(db);
+            return ERROR_DB_CREATE_TEAMS;
+        }
+    }
     sqlite3_finalize(res);
     sqlite3_close(db);
     return SUCCESS;

+ 14 - 3
src/RuneOptimizer/update/update.h

@@ -235,16 +235,27 @@ int calculate_current_stats();
 
 /**
  * Recreates the tables in the database.
+ *
+ * @param[in] clear_teams Indicator to recreate tables teams and units_teams.
+ * @return SUCCESS or an error code.
  */
-int recreate_tables();
+int recreate_tables(unsigned char clear_teams);
 
 /**
  * Updates the database from a json file.
  *
  * @param[in] file Path to the file.
- * @return SUCCESS or an error code..
+ * @param[in] six_stars Indicator to import only 6* units. Units with runes will
+ * be saved anyway.
+ * @param[in] with_runes Indicator to import only units with runes.
+ * @param[in] clear_teams Indicator to recreate tables teams and units_teams.
+ * @param[in] gui Indicator to format output for thr GUI.
+ * @return SUCCESS or an error code.
  */
-int update_json(char file[]);
+int update_json(
+  char file[], unsigned char six_stars,
+  unsigned char with_runes, unsigned char clear_teams, unsigned char gui
+);
 
 /**
  * Calculates the total stat values for a rune.

+ 5 - 0
src/RuneOptimizerGUI/RuneOptimizer.py

@@ -20,12 +20,16 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 import wx
 import wx.grid
+import wx.adv
 import sqlite3
 import math
 import subprocess
+from subprocess import Popen, PIPE
 import json
 import os
+import pipes
 from types import SimpleNamespace
+from time import sleep
 
 #exec(compile(source=open(os.path.dirname(os.path.realpath(__file__)) + '/frames/RuneOptimizerFrame.py').read(), filename=os.path.dirname(os.path.realpath(__file__)) + '/frames/RuneOptimizerFrame.py', mode='exec'))
 #exec(compile(source=open(os.path.dirname(os.path.realpath(__file__)) + '/frames/ResultsFrame.py').read(), filename=os.path.dirname(os.path.realpath(__file__)) + '/frames/ResultsFrame.py', mode='exec'))
@@ -35,6 +39,7 @@ exec(compile(source=open(os.path.dirname(os.path.realpath(__file__)) + '/classes
 exec(compile(source=open(os.path.dirname(os.path.realpath(__file__)) + '/classes/PanelResults.py').read(), filename=os.path.dirname(os.path.realpath(__file__)) + '/classes/PanelResults.py', mode='exec'))
 exec(compile(source=open(os.path.dirname(os.path.realpath(__file__)) + '/classes/RuneOptimizerFrame.py').read(), filename=os.path.dirname(os.path.realpath(__file__)) + '/classes/RuneOptimizerFrame.py', mode='exec'))
 exec(compile(source=open(os.path.dirname(os.path.realpath(__file__)) + '/classes/TabList.py').read(), filename=os.path.dirname(os.path.realpath(__file__)) + '/classes/TabList.py', mode='exec'))
+exec(compile(source=open(os.path.dirname(os.path.realpath(__file__)) + '/classes/DialogUpdateJson.py').read(), filename=os.path.dirname(os.path.realpath(__file__)) + '/classes/DialogUpdateJson.py', mode='exec'))
 
 conn = None
 

+ 139 - 0
src/RuneOptimizerGUI/classes/DialogUpdateJson.py

@@ -0,0 +1,139 @@
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer 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 Foundation, either version 3 of the License, or (at your option)
+any later version.
+
+RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+class DialogUpdateJson(wx.Dialog):
+    """
+    The optimizer form Panel.
+
+    Parameters
+    ----------
+    fileSelector : wxFilePickerCtrl.
+        File selector
+    unitsStars : wx.Checkbox.
+        Checkbox to indicate to save only units with 6 stars.
+    unitsStars : wx.Checkbox.
+        Checkbox to indicate to save only units with runes.
+    clearTeams : wx.Checkbox.
+        Checkbox to indicate team deletion.
+
+    """
+
+    fileSelector = None
+    updateDone = False
+    updateError = False
+
+    def __init__(self, parent, id=wx.ID_ANY):
+        """Initializes the panel.
+
+        Sets upt all the widgets.
+
+        """
+
+        # Parent constructor
+        wx.Dialog.__init__(
+          self, parent=parent, id=id, pos=(50, 50), size=(400, 290)
+        )
+
+        self.form = wx.BoxSizer(wx.VERTICAL)
+        self.progress = wx.BoxSizer(wx.VERTICAL)
+        self.messages = wx.BoxSizer(wx.VERTICAL)
+        self.buttonSizer = wx.BoxSizer(wx.VERTICAL)
+
+        fileSelectorLabel = wx.StaticText(
+            parent=self,  id=wx.ID_ANY, label="Select a JSON file",
+            pos=(30, 30), size=(130, 30)
+          )
+        self.form.Add(fileSelectorLabel)
+        self.fileSelector = wx.FilePickerCtrl(parent=self,
+          id=wx.ID_ANY, path="",
+          message="Select JSON file", wildcard="JSON files (*.json)|*.json",
+          style=wx.FC_DEFAULT_STYLE, pos=(140, 20), size=(250, 40)
+        )
+        self.form.Add(self.fileSelector)
+        self.unitsStars = wx.CheckBox(
+          parent=self, id=wx.ID_ANY, label="Only import units at with 6 stars.",
+          pos=(30, 80), size=(230, 20)
+        )
+        self.form.Add(self.unitsStars)
+        self.unitsRunes = wx.CheckBox(
+          parent=self, id=wx.ID_ANY, label="Only import units with runes.",
+          pos=(30, 110), size=(230, 20)
+        )
+        self.form.Add(self.unitsRunes)
+        self.clearTeams = wx.CheckBox(
+          parent=self, id=wx.ID_ANY, label="Clear team data",
+          pos=(30, 140), size=(230, 20)
+        )
+        self.form.Add(self.clearTeams)
+
+        self.btAccept = wx.Button(
+          parent=self, id=wx.ID_ANY, pos=(100, 180),
+          size=(100, 40), style=wx.LC_REPORT, label="Accept"
+        )
+        btClose = wx.Button(
+          parent=self, id=wx.ID_OK, pos=(210, 180),
+          size=(100, 40), style=wx.LC_REPORT, label="Close"
+        )
+        self.buttonSizer.Add(self.btAccept)
+        self.Bind(wx.EVT_BUTTON, self.accept, self.btAccept)
+
+        # Progress message
+        self.message = wx.StaticText(
+          parent=self,  id=wx.ID_ANY, label="",
+          pos=(20, 20), size=(340, 460)
+        )
+        self.messages.Add(self.message)
+
+    def accept(self, event):
+        self.form.ShowItems(False)
+        self.buttonSizer.ShowItems(False)
+        self.Update()
+        command = "RuneOptimizer update "
+        command += pipes.quote(self.fileSelector.GetPath())
+        if self.unitsStars.GetValue():
+            command += " --six-stars"
+        if self.unitsRunes.GetValue():
+            command += " --with-runes"
+        if self.clearTeams.GetValue():
+            command += " --clear-teams"
+        command += " --gui"
+        command = \
+          os.path.dirname(os.path.realpath(__file__)) + "/../../" + command
+        print("Command: " + command)
+        process = \
+          subprocess.Popen(command.split(),shell=False,stdout=subprocess.PIPE)
+
+        # Poll process.stdout to show stdout live
+        while True:
+            output = process.stdout.readline()
+            if process.poll() is not None:
+                break
+            if output:
+                self.message.SetLabel(self.message.GetLabel() + bytes.decode(output.strip()) + "\n")
+            self.Update()
+        self.Update()
+        rc = process.poll()
+
+        if process.returncode == 0:
+            self.message.SetLabel(self.message.GetLabel() + "\nAll done!")
+            self.updateDone = True
+            self.updateError = False
+        else:
+            self.updateDone = True
+            self.updateError = True
+

+ 2 - 2
src/RuneOptimizerGUI/classes/PanelUnits.py

@@ -498,7 +498,7 @@ class PanelUnits(wx.Panel):
         if self.filterNoTeams.GetValue() == False:
             query += " AND id IN (SELECT DISTINCT unit FROM units_teams) "
         query += " ORDER BY priority DESC; ";
-        #print(query)
+        print(query)
         cursor = conn.execute(query)
         i = 0
         self.unitList.DeleteAllItems()
@@ -507,7 +507,7 @@ class PanelUnits(wx.Panel):
             self.unitList.SetItem(i, 1, str(row[2]))
             self.unitList.SetItem(i, 2, "")
             self.unitList.SetItemData(i, int(row[0]))
-            if (int(row[3]) == 1):
+            if (row[3] == 1):
                 self.unitList.SetItem(i, 2, "X")
             else:
                 self.unitList.SetItem(i, 2, " ")

+ 33 - 3
src/RuneOptimizerGUI/classes/RuneOptimizerFrame.py

@@ -114,11 +114,30 @@ class RuneOptimizerFrame(wx.Frame):
         global conn
         super(RuneOptimizerFrame, self).__init__(*args, **kw)
 
-        # Create and configure a
+        # Create and configure the menue
         pnl = wx.Panel(self)
         self.makeMenuBar()
+
+        # Create the statsusbar
+        status = "Status: "
+        cursor = conn.execute("""
+          SELECT
+            player_id,
+            player_name,
+            player_level,
+            ts,
+            modified
+          FROM info
+        """)
+        row = cursor.fetchone()
+        status = row[1] + ", Lv" + str(row[2]) + " (ID #" +str(row[0]) + ")."
+        status += " Last updated " + str(row[3])
+        if (row[4] == 1):
+            status += ". Modifications aplied since."
+        else:
+            status += ". No modifications aplied since."
         self.CreateStatusBar()
-        self.SetStatusText("Status: Updated, no pending changes")
+        self.SetStatusText(status)
 
         # Create tabs
         tabs = TabList(
@@ -211,7 +230,18 @@ class RuneOptimizerFrame(wx.Frame):
 
         """
 
-        self.showUnimplemented(wx.EVT_MENU)
+        with DialogUpdateJson(self) as dlg:
+            dlg.ShowModal()
+                # do something here
+            print("UPDATE: " + str(dlg.updateDone))
+            print("ERROR: " + str(dlg.updateError))
+            if (dlg.updateDone == True and dlg.updateError == False):
+                print("Update succesfull!")
+            #print('UPDATE!')
+            #print(dlg.fileSelector.GetPath())
+            # TODO
+            # Validate file, prepare command and pass it to RuneOptimizer.
+            # Then, redraw almost all windows
 
     def updateFromSwdb(self, event):
         """

BIN
src/RuneOptimizerGUI/res/icon/progress.gif