| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- #include <flip_world.h>
- #include <flip_storage/storage.h>
- char *fps_choices_str[] = {"30", "60", "120", "240"};
- uint8_t fps_index = 0;
- char *yes_or_no_choices[] = {"No", "Yes"};
- uint8_t screen_always_on_index = 1;
- uint8_t sound_on_index = 1;
- uint8_t vibration_on_index = 1;
- char *player_sprite_choices[] = {"naked", "sword", "axe", "bow"};
- uint8_t player_sprite_index = 1;
- char *vgm_levels[] = {"-2", "-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"};
- uint8_t vgm_x_index = 2;
- uint8_t vgm_y_index = 2;
- uint8_t game_mode_index = 0;
- float atof_(const char *nptr) { return (float)strtod(nptr, NULL); }
- float atof_furi(const FuriString *nptr) { return atof_(furi_string_get_cstr(nptr)); }
- bool is_str(const char *src, const char *dst) { return strcmp(src, dst) == 0; }
- bool is_enough_heap(size_t heap_size, bool check_blocks)
- {
- const size_t min_heap = heap_size + 1024; // 1KB buffer
- const size_t min_free = memmgr_get_free_heap();
- if (min_free < min_heap)
- {
- FURI_LOG_E(TAG, "Not enough heap memory: There are %zu bytes free.", min_free);
- return false;
- }
- if (check_blocks)
- {
- const size_t max_free_block = memmgr_heap_get_max_free_block();
- if (max_free_block < min_heap)
- {
- FURI_LOG_E(TAG, "Not enough free blocks: %zu bytes", max_free_block);
- return false;
- }
- }
- return true;
- }
- static DateTime *flip_world_get_rtc_time()
- {
- DateTime *rtc_time = malloc(sizeof(DateTime));
- if (!rtc_time)
- {
- FURI_LOG_E(TAG, "Failed to allocate memory for DateTime");
- return NULL;
- }
- furi_hal_rtc_get_datetime(rtc_time);
- return rtc_time;
- }
- static DateTime *flip_world_json_to_datetime(const char *str)
- {
- DateTime *rtc_time = malloc(sizeof(DateTime));
- if (!rtc_time)
- {
- FURI_LOG_E(TAG, "Failed to allocate memory for DateTime");
- return NULL;
- }
- char *hour = get_json_value("hour", str);
- char *minute = get_json_value("minute", str);
- char *second = get_json_value("second", str);
- char *day = get_json_value("day", str);
- char *month = get_json_value("month", str);
- char *year = get_json_value("year", str);
- char *weekday = get_json_value("weekday", str);
- if (!hour || !minute || !second || !day || !month || !year || !weekday)
- {
- FURI_LOG_E(TAG, "Failed to parse JSON");
- free(rtc_time);
- return NULL;
- }
- rtc_time->hour = atoi(hour);
- rtc_time->minute = atoi(minute);
- rtc_time->second = atoi(second);
- rtc_time->day = atoi(day);
- rtc_time->month = atoi(month);
- rtc_time->year = atoi(year);
- rtc_time->weekday = atoi(weekday);
- free(hour);
- free(minute);
- free(second);
- free(day);
- free(month);
- free(year);
- free(weekday);
- return rtc_time;
- }
- static FuriString *flip_world_datetime_to_json(DateTime *rtc_time)
- {
- if (!rtc_time)
- {
- FURI_LOG_E(TAG, "rtc_time is NULL");
- return NULL;
- }
- FuriString *json = furi_string_alloc();
- if (!json)
- {
- FURI_LOG_E(TAG, "Failed to allocate memory for JSON");
- return NULL;
- }
- furi_string_printf(
- json,
- "{\"hour\":%d,\"minute\":%d,\"second\":%d,\"day\":%d,\"month\":%d,\"year\":%d,\"weekday\":%d}",
- rtc_time->hour,
- rtc_time->minute,
- rtc_time->second,
- rtc_time->day,
- rtc_time->month,
- rtc_time->year,
- rtc_time->weekday);
- return json;
- }
- static bool flip_world_save_rtc_time(DateTime *rtc_time)
- {
- if (!rtc_time)
- {
- FURI_LOG_E(TAG, "rtc_time is NULL");
- return false;
- }
- FuriString *json = flip_world_datetime_to_json(rtc_time);
- if (!json)
- {
- FURI_LOG_E(TAG, "Failed to convert DateTime to JSON");
- return false;
- }
- save_char("last_checked", furi_string_get_cstr(json));
- furi_string_free(json);
- return true;
- }
- //
- // Returns true if time_current is one hour (or more) later than the stored last_updated time
- //
- static bool flip_world_is_update_time(DateTime *time_current)
- {
- if (!time_current)
- {
- FURI_LOG_E(TAG, "time_current is NULL");
- return false;
- }
- char last_updated_old[32];
- if (!load_char("last_updated", last_updated_old, sizeof(last_updated_old)))
- {
- FURI_LOG_E(TAG, "Failed to load last_updated");
- FuriString *json = flip_world_datetime_to_json(time_current);
- if (json)
- {
- save_char("last_updated", furi_string_get_cstr(json));
- furi_string_free(json);
- }
- return false;
- }
- DateTime *last_updated_time = flip_world_json_to_datetime(last_updated_old);
- if (!last_updated_time)
- {
- FURI_LOG_E(TAG, "Failed to convert JSON to DateTime");
- return false;
- }
- bool time_diff = false;
- // If the date is different assume more than one hour has passed.
- if (time_current->year != last_updated_time->year ||
- time_current->month != last_updated_time->month ||
- time_current->day != last_updated_time->day)
- {
- time_diff = true;
- }
- else
- {
- // For the same day, compute seconds from midnight.
- int seconds_current = time_current->hour * 3600 + time_current->minute * 60 + time_current->second;
- int seconds_last = last_updated_time->hour * 3600 + last_updated_time->minute * 60 + last_updated_time->second;
- if ((seconds_current - seconds_last) >= 3600)
- {
- time_diff = true;
- }
- }
- if (time_diff)
- {
- FuriString *json = flip_world_datetime_to_json(time_current);
- if (json)
- {
- save_char("last_updated", furi_string_get_cstr(json));
- furi_string_free(json);
- }
- }
- free(last_updated_time);
- return time_diff;
- }
- // Sends a request to fetch the last updated date of the app.
- static bool flip_world_last_app_update(FlipperHTTP *fhttp, bool flipper_server)
- {
- if (!fhttp)
- {
- FURI_LOG_E(TAG, "fhttp is NULL");
- return false;
- }
- return flipper_http_request(
- fhttp,
- GET,
- !flipper_server ? "https://www.jblanked.com/flipper/api/app/last-updated/flip_world/"
- : "//--TODO--//",
- "{\"Content-Type\":\"application/json\"}",
- NULL);
- }
- // Parses the server response and returns true if an update is available.
- static bool flip_world_parse_last_app_update(FlipperHTTP *fhttp, DateTime *time_current)
- {
- if (!fhttp)
- {
- FURI_LOG_E(TAG, "fhttp is NULL");
- return false;
- }
- if (fhttp->state == ISSUE)
- {
- FURI_LOG_E(TAG, "Failed to fetch last app update");
- return false;
- }
- if (fhttp->last_response == NULL || strlen(fhttp->last_response) == 0)
- {
- FURI_LOG_E(TAG, "fhttp->last_response is NULL or empty");
- return false;
- }
- // Save the server app version.
- save_char("server_app_version", fhttp->last_response);
- // Only check for an update if an hour or more has passed.
- if (flip_world_is_update_time(time_current))
- {
- char app_version[32];
- if (!load_char("app_version", app_version, sizeof(app_version)))
- {
- FURI_LOG_E(TAG, "Failed to load app version");
- return false;
- }
- if (strcmp(fhttp->last_response, app_version) != 0)
- {
- FURI_LOG_I(TAG, "Update available");
- return true;
- }
- else
- {
- FURI_LOG_I(TAG, "No update available");
- return false;
- }
- }
- return false; // Not yet time to update.
- }
- static bool flip_world_get_fap_file(FlipperHTTP *fhttp, bool flipper_server)
- {
- if (!fhttp)
- {
- FURI_LOG_E(TAG, "FlipperHTTP is NULL.");
- return false;
- }
- char url[256];
- fhttp->save_received_data = false;
- fhttp->is_bytes_request = true;
- #ifndef FW_ORIGIN_Momentum
- snprintf(
- fhttp->file_path,
- sizeof(fhttp->file_path),
- STORAGE_EXT_PATH_PREFIX "/apps/GPIO/flip_world.fap");
- #else
- snprintf(
- fhttp->file_path,
- sizeof(fhttp->file_path),
- STORAGE_EXT_PATH_PREFIX "/apps/GPIO/FlipperHTTP/flip_world.fap");
- #endif
- if (flipper_server)
- {
- char build_id[32];
- snprintf(build_id, sizeof(build_id), "%s", "67f22eb325a4a6f1fb4a2c5d");
- uint8_t target;
- target = furi_hal_version_get_hw_target();
- uint16_t api_major, api_minor;
- furi_hal_info_get_api_version(&api_major, &api_minor);
- snprintf(
- url,
- sizeof(url),
- "https://catalog.flipperzero.one/api/v0/application/version/%s/build/compatible?target=f%d&api=%d.%d",
- build_id,
- target,
- api_major,
- api_minor);
- }
- else
- {
- snprintf(url, sizeof(url), "https://www.jblanked.com/flipper/api/app/download/flip_world");
- }
- return flipper_http_request(fhttp, BYTES, url, "{\"Content-Type\": \"application/octet-stream\"}", NULL);
- }
- // Updates the app. Uses the supplied current time for validating if update check should proceed.
- static void flip_world_update_app(FlipperHTTP *fhttp, DateTime *time_current)
- {
- if (!fhttp)
- {
- FURI_LOG_E(TAG, "fhttp is NULL");
- return;
- }
- if (!flip_world_last_app_update(fhttp, false))
- {
- FURI_LOG_E(TAG, "Failed to fetch last app update");
- return;
- }
- while (fhttp->state == RECEIVING && furi_timer_is_running(fhttp->get_timeout_timer) > 0)
- {
- // Wait for the request to finish.
- furi_delay_ms(100);
- }
- furi_timer_stop(fhttp->get_timeout_timer);
- if (flip_world_parse_last_app_update(fhttp, time_current))
- {
- easy_flipper_dialog("App Update", "FlipWorld update is available.\nPress BACK to install.");
- if (!flip_world_get_fap_file(fhttp, false))
- {
- FURI_LOG_E(TAG, "Failed to fetch fap file");
- return;
- }
- while (fhttp->state == RECEIVING && furi_timer_is_running(fhttp->get_timeout_timer) > 0)
- {
- furi_delay_ms(100);
- }
- furi_timer_stop(fhttp->get_timeout_timer);
- if (fhttp->state == ISSUE)
- {
- FURI_LOG_E(TAG, "Failed to fetch fap file");
- return;
- }
- easy_flipper_dialog("Update Done", "FlipWorld update is done.\nPress BACK to restart.");
- }
- else
- {
- FURI_LOG_I(TAG, "No update available");
- }
- }
- // Handles the app update routine. This function obtains the current RTC time,
- // checks the "last_checked" value, and if it is more than one hour old, calls for an update.
- bool flip_world_handle_app_update(FlipperHTTP *fhttp)
- {
- if (!fhttp)
- {
- FURI_LOG_E(TAG, "fhttp is NULL");
- return false;
- }
- DateTime *rtc_time = flip_world_get_rtc_time();
- if (!rtc_time)
- {
- FURI_LOG_E(TAG, "Failed to get RTC time");
- return false;
- }
- char last_checked[32];
- if (!load_char("last_checked", last_checked, sizeof(last_checked)))
- {
- // First time – save the current time and check for an update.
- flip_world_save_rtc_time(rtc_time);
- flip_world_update_app(fhttp, rtc_time);
- free(rtc_time);
- return true;
- }
- else
- {
- // Check if the current RTC time is at least one hour past the stored time.
- if (flip_world_is_update_time(rtc_time))
- {
- flip_world_update_app(fhttp, rtc_time);
- free(rtc_time);
- return true;
- }
- free(rtc_time);
- return false; // No update necessary.
- }
- }
|