From f1df19f07cf85576dcc4e78acf2a732be2421d3a Mon Sep 17 00:00:00 2001 From: skarg Date: Mon, 22 Jan 2007 16:51:08 +0000 Subject: [PATCH] Moved BACnet date and time into their own module. --- bacnet-stack/bacapp.c | 131 +------------ bacnet-stack/bacapp.h | 26 --- bacnet-stack/bacdef.h | 21 -- bacnet-stack/datetime.c | 394 ++++++++++++++++++++++++++++++++++++++ bacnet-stack/datetime.h | 98 ++++++++++ bacnet-stack/datetime.ide | Bin 0 -> 30644 bytes 6 files changed, 493 insertions(+), 177 deletions(-) create mode 100644 bacnet-stack/datetime.c create mode 100644 bacnet-stack/datetime.h create mode 100644 bacnet-stack/datetime.ide diff --git a/bacnet-stack/bacapp.c b/bacnet-stack/bacapp.c index eee80ba4..efb850e1 100644 --- a/bacnet-stack/bacapp.c +++ b/bacnet-stack/bacapp.c @@ -42,6 +42,7 @@ #include "bacdef.h" #include "bacapp.h" #include "bactext.h" +#include "datetime.h" int bacapp_encode_application_data(uint8_t * apdu, BACNET_APPLICATION_DATA_VALUE * value) @@ -362,136 +363,6 @@ int bacapp_data_len(uint8_t *apdu, int max_apdu_len, return total_len; } -/* returns true if matching or same, false if different */ -bool bacapp_same_date(BACNET_DATE * date1, BACNET_DATE * date2) -{ - bool status = false; - - if (date1 && date2) { - status = true; - if (date1->year != date2->year) - status = false; - if (date1->month != date2->month) - status = false; - if (date1->day != date2->day) - status = false; - if (date1->wday != date2->wday) - status = false; - } - - return status; -} - -/* returns true if matching or same, false if different */ -bool bacapp_same_time(BACNET_TIME * time1, BACNET_TIME * time2) -{ - bool status = false; - - if (time1 && time2) { - status = true; - if (time1->hour != time2->hour) - status = false; - if (time1->min != time2->min) - status = false; - if (time1->sec != time2->sec) - status = false; - if (time1->hundredths != time2->hundredths) - status = false; - } - - return status; -} - -bool bacapp_same_datetime(BACNET_DATE_TIME * datetime1, - BACNET_DATE_TIME * datetime2) -{ - return (bacapp_same_time(&datetime1->time,&datetime2->time) && - bacapp_same_date(&datetime1->date,&datetime2->date)); -} - -void bacapp_copy_date(BACNET_DATE * dest_date, BACNET_DATE * src_date) -{ - if (dest_date && src_date) { - dest_date->year = src_date->year; - dest_date->month = src_date->month; - dest_date->day = src_date->day; - dest_date->wday = src_date->wday; - } -} - -void bacapp_copy_time(BACNET_TIME * dest_time, BACNET_TIME * src_time) -{ - if (dest_time && src_time) { - dest_time->hour = src_time->hour; - dest_time->min = src_time->min; - dest_time->sec = src_time->sec; - dest_time->hundredths = src_time->hundredths; - } -} - -void bacapp_copy_datetime( - BACNET_DATE_TIME * dest_datetime, - BACNET_DATE_TIME * src_datetime) -{ - bacapp_copy_time(&dest_datetime->time,&src_datetime->time); - bacapp_copy_date(&dest_datetime->date,&src_datetime->date); -} - -void bacapp_set_date(BACNET_DATE * bdate, - uint16_t year, uint8_t month, uint8_t day, uint8_t wday) -{ - if (bdate) { - bdate->year = year; - bdate->month = month; - bdate->day = day; - bdate->wday = wday; - } -} - -void bacapp_set_time(BACNET_TIME * btime, - uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths) -{ - if (btime) { - btime->hour = hour; - btime->min = minute; - btime->sec = seconds; - btime->hundredths = hundredths; - } -} - -void bacapp_set_datetime(BACNET_DATE_TIME * bdatetime, - BACNET_DATE * bdate, - BACNET_TIME * btime) -{ - if (bdate && btime && bdatetime) { - bdatetime->time.hour = btime->hour; - bdatetime->time.min = btime->min; - bdatetime->time.sec = btime->sec; - bdatetime->time.hundredths = btime->hundredths; - bdatetime->date.year = bdate->year; - bdatetime->date.month = bdate->month; - bdatetime->date.day = bdate->day; - bdatetime->date.wday = bdate->wday; - } -} - -void bacapp_set_datetime_values(BACNET_DATE_TIME * bdatetime, - uint16_t year, uint8_t month, uint8_t day, uint8_t wday, - uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths) -{ - if (bdatetime) { - bdatetime->date.year = year; - bdatetime->date.month = month; - bdatetime->date.day = day; - bdatetime->date.wday = wday; - bdatetime->time.hour = hour; - bdatetime->time.min = minute; - bdatetime->time.sec = seconds; - bdatetime->time.hundredths = hundredths; - } -} - - #ifdef BACAPP_PRINT_ENABLED bool bacapp_print_value(FILE * stream, BACNET_APPLICATION_DATA_VALUE * value, BACNET_PROPERTY_ID property) diff --git a/bacnet-stack/bacapp.h b/bacnet-stack/bacapp.h index f4a58134..99e78b0f 100644 --- a/bacnet-stack/bacapp.h +++ b/bacnet-stack/bacapp.h @@ -87,30 +87,6 @@ extern "C" { int bacapp_data_len(uint8_t *apdu, int max_apdu_len, BACNET_PROPERTY_ID property); - /* utility comparison functions */ - bool bacapp_same_date(BACNET_DATE * date1, BACNET_DATE * date2); - bool bacapp_same_time(BACNET_TIME * time1, BACNET_TIME * time2); - bool bacapp_same_datetime( - BACNET_DATE_TIME * datetime1, - BACNET_DATE_TIME * datetime2); - - void bacapp_copy_date(BACNET_DATE * date1, BACNET_DATE * date2); - void bacapp_copy_time(BACNET_TIME * time1, BACNET_TIME * time2); - void bacapp_copy_datetime( - BACNET_DATE_TIME * datetime1, - BACNET_DATE_TIME * datetime2); - - void bacapp_set_date(BACNET_DATE * bdate, - uint16_t year, uint8_t month, uint8_t day, uint8_t wday); - void bacapp_set_time(BACNET_TIME * btime, - uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths); - void bacapp_set_datetime(BACNET_DATE_TIME * bdatetime, - BACNET_DATE * bdate, - BACNET_TIME * btime); - void bacapp_set_datetime_values(BACNET_DATE_TIME * bdatetime, - uint16_t year, uint8_t month, uint8_t day, uint8_t wday, - uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths); - #if PRINT_ENABLED #define BACAPP_PRINT_ENABLED #else @@ -133,8 +109,6 @@ extern "C" { #ifdef TEST #include "ctest.h" - bool bacapp_same_time(BACNET_TIME * time1, BACNET_TIME * time2); - bool bacapp_same_date(BACNET_DATE * date1, BACNET_DATE * date2); bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE * value, BACNET_APPLICATION_DATA_VALUE * test_value); diff --git a/bacnet-stack/bacdef.h b/bacnet-stack/bacdef.h index 875bc3e7..dc489ed6 100644 --- a/bacnet-stack/bacdef.h +++ b/bacnet-stack/bacdef.h @@ -86,27 +86,6 @@ struct BACnet_Device_Address { }; typedef struct BACnet_Device_Address BACNET_ADDRESS; -/* date */ -typedef struct BACnet_Date { - uint16_t year; /* AD */ - uint8_t month; /* 1=Jan */ - uint8_t day; /* 1..31 */ - uint8_t wday; /* 1=Monday-7=Sunday */ -} BACNET_DATE; - -/* time */ -typedef struct BACnet_Time { - uint8_t hour; - uint8_t min; - uint8_t sec; - uint8_t hundredths; -} BACNET_TIME; - -typedef struct BACnet_DateTime { - BACNET_DATE date; - BACNET_TIME time; -} BACNET_DATE_TIME; - /* note: with microprocessors having lots more code space than memory, it might be better to have a packed encoding with a library to easily access the data. */ diff --git a/bacnet-stack/datetime.c b/bacnet-stack/datetime.c new file mode 100644 index 00000000..a173781c --- /dev/null +++ b/bacnet-stack/datetime.c @@ -0,0 +1,394 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + This program 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 2 + of the License, or (at your option) any later version. + + This program 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 this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include +#include "datetime.h" + +/* BACnet Date */ +/* year = years since 1900 */ +/* month 1=Jan */ +/* day = day of month 1..31 */ +/* wday 1=Monday...7=Sunday */ + +static bool is_leap_year(uint16_t year) +{ + if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) + return (true); + else + return (false); +} + +static uint8_t month_days(uint16_t year, uint8_t month) +{ + /* note: start with a zero in the first element to save us from a + month - 1 calculation in the lookup */ + int month_days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + /* February */ + if ((month == 2) && is_leap_year(year)) + return 29; + else if (month >= 1 && month <= 12) + return month_days[month]; + else + return 0; +} + +static uint32_t days_since_epoch(uint16_t year, uint8_t month, uint8_t day) +{ + uint32_t days = 0; /* return value */ + uint8_t monthdays; /* days in a month */ + uint16_t years = 0; /* loop counter for years */ + uint8_t months = 0; /* loop counter for months */ + + monthdays = month_days(year, month); + if ((year >= 1900) && (monthdays) && + (day >= 1) && (day <= monthdays)) + { + for (years = 1900; years < year; years++) + { + days += 365; + if (is_leap_year(years)) + days++; + } + for (months = 1; months < month; months++) + { + days += month_days(months,year); + } + days += (day - 1); + } + + return (days); +} + +/* Jan 1, 1900 is a Monday */ +/* wday 1=Monday...7=Sunday */ +static uint8_t day_of_week(uint16_t year, uint8_t month, uint8_t day) +{ + return ((days_since_epoch(year, month, day)%7)+1); +} + +/* if the date1 is the same as date2, return is 0 + if date1 is after date2, returns positive + if date1 is before date2, returns negative */ +int datetime_compare_date(BACNET_DATE * date1, BACNET_DATE * date2) +{ + int diff = 0; + + if (date1 && date2) { + diff = (int)date1->year - (int)date2->year; + if (diff == 0) { + diff = (int)date1->month - (int)date2->month; + if (diff == 0) { + diff = (int)date1->day - (int)date2->day; + } + } + } + + return diff; +} + +/* if the time1 is the same as time2, return is 0 + if time1 is after time2, returns positive + if time1 is before time2, returns negative */ +int datetime_compare_time(BACNET_TIME * time1, BACNET_TIME * time2) +{ + int diff = 0; + + if (time1 && time2) { + diff = (int)time1->hour - (int)time2->hour; + if (diff == 0) { + diff = (int)time1->min - (int)time2->min; + if (diff == 0) { + diff = (int)time1->sec - (int)time2->sec; + if (diff == 0) { + diff = (int)time1->hundredths - (int)time2->hundredths; + } + } + } + } + + return diff; +} + +/* if the datetime1 is the same as datetime2, return is 0 + if datetime1 is before datetime2, returns negative + if datetime1 is after datetime2, returns positive */ +bool datetime_compare(BACNET_DATE_TIME * datetime1, + BACNET_DATE_TIME * datetime2) +{ + int diff = 0; + + diff = datetime_compare_date(&datetime1->date,&datetime2->date); + if (diff == 0) { + diff = datetime_compare_time(&datetime1->time,&datetime2->time); + } + + return diff; +} + +void datetime_copy_date(BACNET_DATE * dest_date, BACNET_DATE * src_date) +{ + if (dest_date && src_date) { + dest_date->year = src_date->year; + dest_date->month = src_date->month; + dest_date->day = src_date->day; + dest_date->wday = src_date->wday; + } +} + +void datetime_copy_time(BACNET_TIME * dest_time, BACNET_TIME * src_time) +{ + if (dest_time && src_time) { + dest_time->hour = src_time->hour; + dest_time->min = src_time->min; + dest_time->sec = src_time->sec; + dest_time->hundredths = src_time->hundredths; + } +} + +void datetime_copy( + BACNET_DATE_TIME * dest_datetime, + BACNET_DATE_TIME * src_datetime) +{ + datetime_copy_time(&dest_datetime->time,&src_datetime->time); + datetime_copy_date(&dest_datetime->date,&src_datetime->date); +} + +void datetime_set_date(BACNET_DATE * bdate, + uint16_t year, uint8_t month, uint8_t day) +{ + if (bdate) { + bdate->year = year; + bdate->month = month; + bdate->day = day; + bdate->wday = day_of_week(year,month,day); + } +} + +void datetime_set_time(BACNET_TIME * btime, + uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths) +{ + if (btime) { + btime->hour = hour; + btime->min = minute; + btime->sec = seconds; + btime->hundredths = hundredths; + } +} + +void datetime_set(BACNET_DATE_TIME * bdatetime, + BACNET_DATE * bdate, + BACNET_TIME * btime) +{ + if (bdate && btime && bdatetime) { + bdatetime->time.hour = btime->hour; + bdatetime->time.min = btime->min; + bdatetime->time.sec = btime->sec; + bdatetime->time.hundredths = btime->hundredths; + bdatetime->date.year = bdate->year; + bdatetime->date.month = bdate->month; + bdatetime->date.day = bdate->day; + bdatetime->date.wday = bdate->wday; + } +} + +void datetime_set_values(BACNET_DATE_TIME * bdatetime, + uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths) +{ + if (bdatetime) { + bdatetime->date.year = year; + bdatetime->date.month = month; + bdatetime->date.day = day; + bdatetime->date.wday = day_of_week(year,month,day); + bdatetime->time.hour = hour; + bdatetime->time.min = minute; + bdatetime->time.sec = seconds; + bdatetime->time.hundredths = hundredths; + } +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testBACnetDate(Test * pTest) +{ + BACNET_DATE bdate1, bdate2; + int diff = 0; + + datetime_set_date(&bdate1, 1900,1,1); + datetime_copy_date(&bdate2, &bdate1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff == 0); + datetime_set_date(&bdate2, 1900,1,2); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 1900,2,1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 1901,1,1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + + datetime_set_date(&bdate1, 2007,7,15); + datetime_copy_date(&bdate2, &bdate1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff == 0); + datetime_set_date(&bdate2, 2007,7,14); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2007,7,1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2007,7,31); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 2007,8,15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 2007,12,15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 2007,6,15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2007,1,15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2006,7,15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 1900,7,15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2008,7,15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 2154,7,15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + + return; +} + +void testBACnetTime(Test * pTest) +{ + BACNET_TIME btime1, btime2; + int diff = 0; + + datetime_set_time(&btime1, 0,0,0,0); + datetime_copy_time(&btime2, &btime1); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff == 0); + + datetime_set_time(&btime1, 23,59,59,99); + datetime_copy_time(&btime2, &btime1); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff == 0); + + + return; +} + +void testBACnetDateTime(Test * pTest) +{ + BACNET_DATE_TIME bdatetime; + BACNET_DATE_TIME test_bdatetime; + int diff = 0; + + (void)pTest; + + return; +} + +void testBACnetDayOfWeek(Test * pTest) +{ + uint8_t dow = 0; + + /* 1/1/1900 is a Monday */ + dow = day_of_week(1900, 1, 1); + ct_test(pTest, dow == 1); + + /* 1/1/2007 is a Monday */ + dow = day_of_week(2007, 1, 1); + ct_test(pTest, dow == 1); + dow = day_of_week(2007, 1, 2); + ct_test(pTest, dow == 2); + dow = day_of_week(2007, 1, 3); + ct_test(pTest, dow == 3); + dow = day_of_week(2007, 1, 4); + ct_test(pTest, dow == 4); + dow = day_of_week(2007, 1, 5); + ct_test(pTest, dow == 5); + dow = day_of_week(2007, 1, 6); + ct_test(pTest, dow == 6); + dow = day_of_week(2007, 1, 7); + ct_test(pTest, dow == 7); + + dow = day_of_week(2007, 1, 31); + ct_test(pTest, dow == 3); + +} + +#ifdef TEST_DATE_TIME +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Date Time", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACnetDayOfWeek); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetDate); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetTime); + assert(rc); + + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_DATE_TIME */ +#endif /* TEST */ diff --git a/bacnet-stack/datetime.h b/bacnet-stack/datetime.h new file mode 100644 index 00000000..782b43d6 --- /dev/null +++ b/bacnet-stack/datetime.h @@ -0,0 +1,98 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + This program 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 2 + of the License, or (at your option) any later version. + + This program 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 this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#ifndef DATETIME_H +#define DATETIME_H + +#include +#include + +/* date */ +typedef struct BACnet_Date { + uint16_t year; /* AD */ + uint8_t month; /* 1=Jan */ + uint8_t day; /* 1..31 */ + uint8_t wday; /* 1=Monday-7=Sunday */ +} BACNET_DATE; + +/* time */ +typedef struct BACnet_Time { + uint8_t hour; + uint8_t min; + uint8_t sec; + uint8_t hundredths; +} BACNET_TIME; + +typedef struct BACnet_DateTime { + BACNET_DATE date; + BACNET_TIME time; +} BACNET_DATE_TIME; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* utility initialization functions */ + void bacapp_set_date(BACNET_DATE * bdate, + uint16_t year, uint8_t month, uint8_t day); + void bacapp_set_time(BACNET_TIME * btime, + uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths); + void bacapp_set_datetime(BACNET_DATE_TIME * bdatetime, + BACNET_DATE * bdate, + BACNET_TIME * btime); + void bacapp_set_datetime_values(BACNET_DATE_TIME * bdatetime, + uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths); + + /* utility comparison functions: + if the date/times are the same, return is 0 + if date1 is before date2, returns negative + if date1 is after date2, returns positive */ + int bacapp_date_compare(BACNET_DATE * date1, BACNET_DATE * date2); + int bacapp_time_compare(BACNET_TIME * time1, BACNET_TIME * time2); + int bacapp_datetime_compare( + BACNET_DATE_TIME * datetime1, + BACNET_DATE_TIME * datetime2); + + /* utility copy functions */ + void bacapp_copy_date(BACNET_DATE * date1, BACNET_DATE * date2); + void bacapp_copy_time(BACNET_TIME * time1, BACNET_TIME * time2); + void bacapp_copy_datetime( + BACNET_DATE_TIME * datetime1, + BACNET_DATE_TIME * datetime2); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* DATETIME_H */ \ No newline at end of file diff --git a/bacnet-stack/datetime.ide b/bacnet-stack/datetime.ide new file mode 100644 index 0000000000000000000000000000000000000000..f0dbecaf27b4326dec857edadad71fcaebf4333e GIT binary patch literal 30644 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 bHvu!h_X3wj@P;iOKV|y=g#XtXh@1Zc6mUMJ literal 0 HcmV?d00001